meta:
  id: finance-bp-078-v5.3
  version: v6.1
  blueprint_id: finance-bp-078
  sop_version: crystal-compilation-v6.1
  source_language: en
  compiled_at: '2026-04-22T13:00:29.702985+00:00'
  target_host: openclaw
  authoritative_artifact:
    primary: seed.yaml
    non_authoritative_derivatives:
    - SKILL.md (host-generated summary, may lag)
    - HEARTBEAT.md (host telemetry)
    - memory/*.md (host conversational memory)
    rule: On any behavioral decision (preconditions check, OV assertion, EQ rule firing, spec_lock verification), agents MUST
      re-read seed.yaml. Derivatives are for UI display only and may be out-of-date.
  execution_protocol:
    install_trigger:
    - Execute resources.host_adapter.install_recipes[] in declared order
    - Verify each package with import check before proceeding
    execute_trigger: When user intent matches intent_router.uc_entries[].positive_terms AND user uses action verb (run/execute/跑/执行/backtest/fetch/collect)
    on_execute:
    - Reload seed.yaml (do not rely on SKILL.md or cached summaries)
    - Run preconditions[] in declared order; halt on first fatal failure with on_fail message to user
    - Enter context_state_machine.CA1_MEMORY_CHECKED state
    - Evaluate evidence_quality.enforcement_rules[]; prepend user_disclosure_template
    - Translate user_facing_fields to user locale per locale_contract
    - "[V6 READING ORDER]\nThis crystal contains the following V6 layers. Before answering any business question, the host\
      \ MUST read them in order:\n  1. anti_patterns[] — cross-project anti-patterns (with AP-* ids)\n  2. cross_project_wisdom[]\
      \ — cross-project wisdom (with CW-* ids)\n  3. domain_constraints_injected[] — domain constraints (SHARED-* ids)\n \
      \ 4. known_use_cases[] — concrete business scenarios (KUC-* ids)\n  5. component_capability_map — AST component map\
      \ (by module)\n\nWhen answering user questions, proactively cite relevant AP-*/CW-*/SHARED-*/KUC-* ids with source text.\
      \ Examples: T+1 rules -> cite SHARED-* constraint; model comparison -> warn via AP-*; follow-holdings strategy -> cite\
      \ KUC-* with example file."
    workspace_resolution:
      scripts_path: '{host_workspace}/scripts/'
      skills_path: '{host_workspace}/skills/'
      trace_path: '{host_workspace}/.trace/'
  capability_tags:
    markets:
    - global
    activities:
    - accounting
  upgraded_from: finance-bp-078-v1.seed.yaml
  upgraded_at: '2026-04-22T13:20:17.397484+00:00'
  v6_inputs:
    ast_mind_map: knowledge/sources/finance/finance-bp-078--fava_investor/v6_inputs/ast_mind_map.yaml
    anti_patterns: null
    cross_project_wisdom: null
    examples_kuc: knowledge/sources/finance/finance-bp-078--fava_investor/v6_inputs/examples_kuc.yaml
    shared_pools_dir: knowledge/sources/finance/_shared
anti_patterns:
- id: AP-ACCOUNTING-001
  title: Using floating-point arithmetic for monetary amounts
  description: Representing currency values with float64 or similar floating-point types causes precision loss during arithmetic
    operations. Rounding errors accumulate over multiple transactions, leading to incorrect balance calculations and potential
    financial losses. This violates the fundamental requirement that monetary calculations must be exact.
  project_source: finance-bp-073--ledger, finance-bp-129--beancount
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-002
  title: Skipping initialization calls before VM/script execution
  description: Executing Numscript VM without first calling ResolveResources() and ResolveBalances() causes panics with ErrResourcesNotInitialized
    or ErrBalancesNotInitialized. This prevents any script execution and leaves transactions in an unrunnable state, blocking
    financial operations entirely.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-003
  title: Mixing different asset types in monetary operations
  description: Performing addition, subtraction, or take operations on amounts with different asset types produces invalid
    financial calculations. This violates the fundamental accounting principle that amounts in different currencies cannot
    be combined, leading to corrupted account balances and failed reconciliations.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-004
  title: Missing insufficient funds validation
  description: Failing to detect when account balance cannot cover a requested withdrawal or transfer allows overdrafts beyond
    permitted limits. This causes real monetary losses, account balance violations, and potential regulatory compliance issues
    in global markets.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-005
  title: Non-atomic transaction commit/rollback
  description: Processing database operations without atomic commit/rollback leaves partial state when failures occur. This
    corrupts account balances and volumes, violating double-entry bookkeeping integrity and making audit trails unreliable
    for global regulatory compliance.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-006
  title: On-demand posting generation causing double-spending
  description: Computing postings on-demand rather than accumulating them during transaction execution fails to track already-spent
    funds within the same transaction. This creates double-spending vulnerabilities that violate atomic transaction semantics
    and can result in significant financial losses.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-007
  title: Log insertion after transaction commit breaking event sourcing
  description: Committing the transaction before inserting the audit log breaks the event sourcing pattern fundamental to
    accounting integrity. This makes it impossible to rebuild state from logs and violates audit requirements necessary for
    global financial compliance.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-008
  title: Incomplete transaction log hash chaining
  description: Computing log hashes without including the previous log hash breaks the immutable audit trail chain. This allows
    undetected tampering with historical transaction records, compromising financial integrity and regulatory audit compliance.
  project_source: finance-bp-073--ledger
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-009
  title: Incorrect row data access patterns on query results
  description: Using dictionary notation (row['column_name']) on namedtuple query results raises TypeError since namedtuples
    only support attribute access. This breaks all module queries expecting attribute-style access, causing asset allocation,
    tax loss harvesting, and other critical financial computations to fail.
  project_source: finance-bp-078--fava_investor
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-010
  title: Missing bidirectional inference for fund relationship declarations
  description: When relationship A→B is declared but B→A is not inferred, the TLH partner list becomes incomplete. This leads
    to suboptimal tax-loss harvesting decisions where only some funds show all valid swap options, reducing potential tax
    savings for investors.
  project_source: finance-bp-078--fava_investor
  severity: medium
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-011
  title: Wash sale comparison within substantially identical groups
  description: Comparing a ticker to itself in its own substantially identical group falsely triggers wash sale warnings.
    This incorrectly blocks valid tax-loss harvesting transactions, causing investors to miss opportunities to realize tax
    losses and offset capital gains.
  project_source: finance-bp-078--fava_investor
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-012
  title: Missing substantially identical tickers in wash sale queries
  description: Omitting substantially identical fund tickers from the wash sale comparison set allows purchases of similar
    funds within the 30-day window. This triggers unintended wash sales that disallow tax loss claims on subsequent sales
    of the original position.
  project_source: finance-bp-078--fava_investor
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-013
  title: Using parsed entries with MISSING sentinel values for calculations
  description: Using parsed entries directly that contain MISSING sentinel values for balance or cost computations causes
    runtime errors or silent zero-value calculations. This results in incorrect portfolio valuations and reconciliation failures,
    compromising financial reporting accuracy.
  project_source: finance-bp-129--beancount
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-014
  title: Underspecified interpolation with multiple missing values per currency
  description: Having more than one missing value per currency group creates an underdetermined system with no unique solution
    during interpolation. This causes InterpolationError and transaction failure, blocking balance calculations for affected
    accounts.
  project_source: finance-bp-129--beancount
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
- id: AP-ACCOUNTING-015
  title: Violating accounting identity in opening balance transactions
  description: Creating opening balance transactions where the total balance of summarized entries does not equal exactly
    zero violates the fundamental accounting identity (Assets = Liabilities + Equity). This causes the balance sheet to be
    fundamentally incorrect with non-zero total assets and liabilities.
  project_source: finance-bp-129--beancount
  severity: high
  applicable_to_tags:
    markets:
    - global
    activities:
    - accounting
  _source_file: anti-patterns/accounting.yaml
cross_project_wisdom:
- wisdom_id: CW-ACCOUNTING-001
  source_project: finance-bp-073--ledger, finance-bp-129--beancount
  pattern_name: Use exact-precision integer types for monetary representation
  description: Both the Numscript ledger and Beancount parser mandates using Decimal (beancount) or MonetaryInt based on big.Int
    (ledger) instead of floating-point. This pattern ensures no rounding errors accumulate in financial calculations, critical
    for audit compliance in global markets.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-002
  source_project: finance-bp-073--ledger
  pattern_name: Mandatory initialization sequence before execution
  description: 'The Numscript VM requires a strict initialization sequence: ResolveResources() then ResolveBalances() must
    both be called before Execute(). Skipping any step causes panics. This teaches that VM/script execution requires careful
    state setup—always verify prerequisites before running financial logic.'
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-003
  source_project: finance-bp-073--ledger
  pattern_name: Dual idempotency key strategy
  description: 'Using both IdempotencyKey and IdempotencyHash together ensures robust duplicate detection: IdempotencyKey
    prevents exact retries while IdempotencyHash catches retries with different input parameters that would otherwise incorrectly
    succeed. Single-key approaches leave gaps in financial transaction safety.'
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-004
  source_project: finance-bp-073--ledger
  pattern_name: Log-before-commit event sourcing pattern
  description: In the transaction processing pipeline, the log must be inserted before committing the transaction to maintain
    event sourcing integrity. This ensures the audit trail can always reconstruct state and supports rollback scenarios, critical
    for regulatory compliance in global accounting.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-005
  source_project: finance-bp-073--ledger
  pattern_name: Read Committed isolation with FOR UPDATE locks
  description: When implementing balance operations, use Read Committed isolation level combined with FOR UPDATE row locks.
    This prevents concurrent transactions from creating inconsistent balances (e.g., both succeeding when they should fail
    due to insufficient funds), ensuring data integrity under concurrent load.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-006
  source_project: finance-bp-078--fava_investor
  pattern_name: Transitive closure for equivalence relationships
  description: When building commodity groups or substantially identical fund relationships, apply transitive closure to infer
    complete equivalence. If A equals B and B equals C, then A, B, and C form one group. This ensures wash sale detection
    and TLH calculations are complete and accurate across all declared relationships.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-007
  source_project: finance-bp-078--fava_investor
  pattern_name: Canonical representative selection for relationship groups
  description: When selecting a representative for a substantially identical fund group, always return the same representative
    ticker for any member of that group. Inconsistent representative selection causes non-deterministic calculations where
    the same ticker gets different partners depending on which group member is queried.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-008
  source_project: finance-bp-129--beancount
  pattern_name: Immutable monetary objects with __slots__
  description: Constructing Amount or Position objects using immutable Decimal values with __slots__ = () pattern prevents
    accidental mutation of monetary values after creation. This immutability ensures financial calculations remain consistent
    throughout transaction processing and audit trails.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-009
  source_project: finance-bp-129--beancount
  pattern_name: Eliminate all MISSING values before presenting parsed data as complete
  description: Parsed entries with MISSING sentinel values are incomplete and cannot be used for financial reporting. All
    MISSING values must be resolved through booking and interpolation before claiming parsed entries are ready for balance
    calculations or realized/unrealized gains computation.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
- wisdom_id: CW-ACCOUNTING-010
  source_project: finance-bp-078--fava_investor, finance-bp-129--beancount
  pattern_name: Strict schema compatibility across class hierarchies
  description: When extending base classes with additional functionality (like ScaledNAV extending RelateTickers), maintain
    compatibility with existing metadata schemas. Schema divergence causes extended classes to miss relationships declared
    for the base class, breaking wash sale detection and TLH recommendations.
  applicable_to_activity: accounting
  _source_file: cross-project-wisdom/accounting.yaml
domain_constraints_injected: []
resources_injected: {}
known_use_cases:
- kuc_id: KUC-101
  source_file: fava_investor/cli/investor.py
  business_problem: Provides a unified command-line interface for portfolio management operations including tax loss harvesting,
    asset allocation analysis, cash drag detection, and tax gain minimization.
  intent_keywords:
  - portfolio management
  - CLI
  - command line
  - tax optimization
  - investment analysis
  stage: data_collection
  data_domain: holding_data
  type: live_trading
- kuc_id: KUC-102
  source_file: fava_investor/util/test_relatetickers.py
  business_problem: Identifies and groups equivalent or substitutable securities (e.g., VTI, VTSAX, VTSMX) based on metadata
    annotations to support tax lot management and wash sale detection.
  intent_keywords:
  - equivalent tickers
  - related securities
  - commodity grouping
  - ticker equivalence
  - substitutable assets
  stage: data_collection
  data_domain: holding_data
  type: data_pipeline
- kuc_id: KUC-103
  source_file: fava_investor/modules/minimizegains/test_minimizegains.py
  business_problem: Determines optimal sell order for securities to minimize realized capital gains by analyzing cost basis
    and holding periods across multiple lots.
  intent_keywords:
  - minimize gains
  - tax-efficient selling
  - capital gains optimization
  - lot selection
  - cost basis optimization
  stage: factor_computation
  data_domain: holding_data
  type: live_trading
- kuc_id: KUC-104
  source_file: fava_investor/modules/assetalloc_class/test_asset_allocation.py
  business_problem: Calculates and reports portfolio allocation breakdown by asset type (stocks, bonds, cash, etc.) with percentage
    distributions from investment account holdings.
  intent_keywords:
  - asset allocation
  - portfolio breakdown
  - asset class distribution
  - allocation report
  - portfolio composition
  stage: factor_computation
  data_domain: holding_data
  type: reporting
- kuc_id: KUC-105
  source_file: fava_investor/modules/tlh/test_libtlh.py
  business_problem: Identifies securities with unrealized losses that can be sold to harvest tax losses, typically looking
    back 30 days to find positions eligible for wash sale rule exceptions.
  intent_keywords:
  - tax loss harvesting
  - loss identification
  - wash sale
  - TLH opportunities
  - tax loss selling
  stage: factor_computation
  data_domain: holding_data
  type: live_trading
component_capability_map:
  project: finance-bp-078--fava_investor
  scan_date: '2026-04-22'
  stats:
    total_files: 8
    total_classes: 35
    total_functions: 0
    total_stages: 8
  modules:
    api_abstraction_layer:
      class_count: 5
      stage_id: api_abstraction
      stage_order: 1
      responsibility: 'Provides unified interface for accessing Beancount ledger data from both Fava web UI and standalone
        CLI. WHY: Enables code reuse while accommodating different runtime contexts - web plugin vs command-line tool share
        the same data access logic.'
      classes:
      - name: FavaInvestorAPI.query_func
        file: api_abstraction_layer/favainvestorapi-query-func.py
        line: 0
        kind: required_method
        signature: ''
      - name: AccAPI.build_price_map
        file: api_abstraction_layer/accapi-build-price-map.py
        line: 0
        kind: required_method
        signature: ''
      - name: AccAPI.realize
        file: api_abstraction_layer/accapi-realize.py
        line: 0
        kind: required_method
        signature: ''
      - name: AccAPI.get_operating_currencies
        file: api_abstraction_layer/accapi-get-operating-currencies.py
        line: 0
        kind: required_method
        signature: ''
      - name: api_implementation
        file: api_abstraction_layer/api-implementation.py
        line: 0
        kind: replaceable_point
      design_decision_count: 3
    ticker_relationship_analyzer:
      class_count: 4
      stage_id: ticker_relationships
      stage_order: 2
      responsibility: 'Infers relationships between investment tickers from incomplete metadata declarations. WHY: Tax loss
        harvesting requires knowing which funds are ''substantially identical'' (trigger wash sales) vs ''substantially different''
        (safe for TLH swaps), and this information should be declarable once and inferr'
      classes:
      - name: RelateTickers.substidenticals
        file: ticker_relationship_analyzer/relatetickers-substidenticals.py
        line: 0
        kind: required_method
        signature: ''
      - name: RelateTickers.representative
        file: ticker_relationship_analyzer/relatetickers-representative.py
        line: 0
        kind: required_method
        signature: ''
      - name: RelateTickers.compute_tlh_groups
        file: ticker_relationship_analyzer/relatetickers-compute-tlh-groups.py
        line: 0
        kind: required_method
        signature: ''
      - name: relationship_source
        file: ticker_relationship_analyzer/relationship-source.py
        line: 0
        kind: replaceable_point
      design_decision_count: 4
    asset_allocation_by_class:
      class_count: 4
      stage_id: asset_allocation_by_class
      stage_order: 3
      responsibility: 'Computes portfolio allocation percentages based on commodity metadata classifications (asset_allocation_*).
        WHY: Enables investors to see if their portfolio matches target allocations without manual spreadsheet work, visualizing
        how their actual holdings compare to target allocations.'
      classes:
      - name: treeify
        file: asset_allocation_by_class/treeify.py
        line: 0
        kind: required_method
        signature: ''
      - name: bucketize
        file: asset_allocation_by_class/bucketize.py
        line: 0
        kind: required_method
        signature: ''
      - name: AssetClassNode.serialise
        file: asset_allocation_by_class/assetclassnode-serialise.py
        line: 0
        kind: required_method
        signature: ''
      - name: bucketize_strategy
        file: asset_allocation_by_class/bucketize-strategy.py
        line: 0
        kind: replaceable_point
      design_decision_count: 4
    asset_allocation_by_account:
      class_count: 5
      stage_id: asset_allocation_by_account
      stage_order: 4
      responsibility: 'Groups account balances into portfolios based on regex patterns or metadata. WHY: Lets investors define
        custom portfolio groupings without changing their account naming conventions, enabling flexible portfolio organization
        independent of chart of accounts structure.'
      classes:
      - name: portfolio_accounts
        file: asset_allocation_by_account/portfolio-accounts.py
        line: 0
        kind: required_method
        signature: ''
      - name: by_account_name
        file: asset_allocation_by_account/by-account-name.py
        line: 0
        kind: required_method
        signature: ''
      - name: by_account_open_metadata
        file: asset_allocation_by_account/by-account-open-metadata.py
        line: 0
        kind: required_method
        signature: ''
      - name: asset_allocation
        file: asset_allocation_by_account/asset-allocation.py
        line: 0
        kind: required_method
        signature: ''
      - name: selection_strategy
        file: asset_allocation_by_account/selection-strategy.py
        line: 0
        kind: replaceable_point
      design_decision_count: 2
    cash_drag_detector:
      class_count: 3
      stage_id: cash_drag
      stage_order: 5
      responsibility: 'Identifies uninvested cash sitting in brokerage accounts that could be deployed for investment. WHY:
        Idle cash loses purchasing power to inflation over time; detecting it enables investors to take action and rebalance
        their portfolios efficiently.'
      classes:
      - name: find_loose_cash
        file: cash_drag_detector/find-loose-cash.py
        line: 0
        kind: required_method
        signature: ''
      - name: find_cash_commodities
        file: cash_drag_detector/find-cash-commodities.py
        line: 0
        kind: required_method
        signature: ''
      - name: cash_definition
        file: cash_drag_detector/cash-definition.py
        line: 0
        kind: replaceable_point
      design_decision_count: 2
    tax_loss_harvester:
      class_count: 7
      stage_id: tax_loss_harvesting
      stage_order: 6
      responsibility: 'Finds investment lots with unrealized losses suitable for tax loss harvesting (TLH). WHY: TLH turns
        paper losses into actual tax deductions, reducing current tax burden in the US SpecID method. Allows investors to
        systematically identify harvest opportunities.'
      classes:
      - name: find_harvestable_lots
        file: tax_loss_harvester/find-harvestable-lots.py
        line: 0
        kind: required_method
        signature: ''
      - name: gain_term
        file: tax_loss_harvester/gain-term.py
        line: 0
        kind: required_method
        signature: ''
      - name: query_recently_bought
        file: tax_loss_harvester/query-recently-bought.py
        line: 0
        kind: required_method
        signature: ''
      - name: recently_sold_at_loss
        file: tax_loss_harvester/recently-sold-at-loss.py
        line: 0
        kind: required_method
        signature: ''
      - name: harvestable_by_commodity
        file: tax_loss_harvester/harvestable-by-commodity.py
        line: 0
        kind: required_method
        signature: ''
      - name: wash_window
        file: tax_loss_harvester/wash-window.py
        line: 0
        kind: replaceable_point
      - name: loss_threshold
        file: tax_loss_harvester/loss-threshold.py
        line: 0
        kind: replaceable_point
      design_decision_count: 5
    gains_minimizer:
      class_count: 3
      stage_id: minimize_gains
      stage_order: 7
      responsibility: 'Determines optimal lot selection to minimize capital gains when selling. WHY: In US SpecID method,
        choosing which lots to sell directly impacts tax liability. This module helps investors minimize their tax burden
        by prioritizing highest-loss lots.'
      classes:
      - name: find_minimized_gains
        file: gains_minimizer/find-minimized-gains.py
        line: 0
        kind: required_method
        signature: ''
      - name: find_tax_burden
        file: gains_minimizer/find-tax-burden.py
        line: 0
        kind: required_method
        signature: ''
      - name: lot_selection_algorithm
        file: gains_minimizer/lot-selection-algorithm.py
        line: 0
        kind: replaceable_point
      design_decision_count: 4
    metadata_summarizer:
      class_count: 4
      stage_id: metadata_summarizer
      stage_order: 8
      responsibility: 'Extracts and displays metadata from account/commodity Open directives as formatted tables. WHY: Allows
        investors to store and view reference info (phone numbers, account numbers, contact details) alongside their ledger,
        making metadata accessible without manual inspection.'
      classes:
      - name: build_tables
        file: metadata_summarizer/build-tables.py
        line: 0
        kind: required_method
        signature: ''
      - name: active_accounts_metadata
        file: metadata_summarizer/active-accounts-metadata.py
        line: 0
        kind: required_method
        signature: ''
      - name: commodities_metadata
        file: metadata_summarizer/commodities-metadata.py
        line: 0
        kind: required_method
        signature: ''
      - name: directive_type
        file: metadata_summarizer/directive-type.py
        line: 0
        kind: replaceable_point
      design_decision_count: 3
  data_flow_hints: []
locale_contract:
  source_language: en
  user_facing_fields:
  - human_summary.what_i_can_do.tagline
  - human_summary.what_i_can_do.use_cases[]
  - human_summary.what_i_auto_fetch[]
  - human_summary.what_i_ask_you[]
  - evidence_quality.user_disclosure_template
  - post_install_notice.message_template.positioning
  - post_install_notice.message_template.capability_catalog.groups[].name
  - post_install_notice.message_template.capability_catalog.groups[].description
  - post_install_notice.message_template.capability_catalog.groups[].ucs[].name
  - post_install_notice.message_template.capability_catalog.groups[].ucs[].short_description
  - post_install_notice.message_template.call_to_action
  - post_install_notice.message_template.featured_entries[].beginner_prompt
  - post_install_notice.message_template.more_info_hint
  - preconditions[].description
  - preconditions[].on_fail
  - intent_router.uc_entries[].name
  - intent_router.uc_entries[].ambiguity_question
  - architecture.pipeline
  - architecture.stages[].narrative.does_what
  - architecture.stages[].narrative.key_decisions
  - architecture.stages[].narrative.common_pitfalls
  - constraints.fatal[].consequence
  - constraints.regular[].consequence
  - output_validator.assertions[].failure_message
  - acceptance.hard_gates[].on_fail
  - skill_crystallization.action
  locale_detection_order:
  - explicit_user_declaration
  - first_message_language
  - system_locale
  translation_enforcement:
    trigger: on_first_user_message
    action: Render user_facing_fields in detected locale, preserving all IDs (BD-/SL-/UC-/finance-C-) and code identifiers
      verbatim
    violation_code: LOCALE-01
    violation_signal: User receives untranslated English Human Summary when detected locale != en
evidence_quality:
  declared:
    evidence_coverage_ratio: 1.0
    evidence_verify_ratio: 0.21551724137931033
    evidence_invalid: 91
    evidence_verified: 25
    evidence_auto_fixed: 0
    audit_coverage: 33/33 (100%)
    audit_pass_rate: 4/33 (12%)
    audit_fail_total: 14
    audit_finance_universal:
      pass: 2
      warn: 11
      fail: 7
    audit_subdomain_totals:
      pass: 2
      warn: 4
      fail: 7
  enforcement_rules:
  - id: EQ-01
    trigger: declared.evidence_verify_ratio < 0.5
    action: MUST invoke traceback lookup for all cited BD-IDs in output before emitting business code — read LATEST.yaml sections
      for each BD referenced
    violation_code: EQ-01-V
    violation_signal: Generated script references BD-IDs but no tool_call to read LATEST.yaml preceded code generation
  user_disclosure_template: '[QUALITY NOTICE] This crystal was compiled from blueprint finance-bp-078. Evidence verify ratio
    = 21.6% and audit fail total = 14. Generated results may have uncaptured requirement gaps. Verify critical decisions against
    source files (LATEST.yaml / LATEST.jsonl).'
traceback:
  source_files:
    blueprint: LATEST.yaml
    constraints: LATEST.jsonl
  mandatory_lookup_scenarios:
  - id: TB-01
    condition: Two constraints have apparently conflicting enforcement rules
    lookup_target: LATEST.jsonl — find both constraint IDs, compare `consequence` + `evidence_refs` to determine priority
  - id: TB-02
    condition: A business decision rationale is unclear or disputed
    lookup_target: LATEST.yaml — locate BD-ID under business_decisions, read `rationale` + `alternative_considered` fields
  - id: TB-03
    condition: evidence_invalid > 0 in evidence_quality.declared
    lookup_target: LATEST.yaml _enrich_meta — cross-check specific BD `evidence_refs` fields for invalid markers
  - id: TB-04
    condition: User asks where a rule comes from
    lookup_target: LATEST.jsonl — find constraint by ID, read `confidence.evidence_refs` for source file + line number
  - id: TB-05
    condition: Generated code does not match expected ZVT API behavior
    lookup_target: LATEST.yaml stages[].required_methods — verify method signature and evidence locator in source code
  degraded_lookup:
    no_fs_access: 'Ask the user to paste the relevant LATEST.yaml section or LATEST.jsonl lines for the BD-/finance-C- IDs
      in question. Crystal ID: finance-bp-078-v5.0.'
trace_schema:
  event_types:
  - precondition_check
  - spec_lock_check
  - evidence_rule_fired
  - evidence_rule_skipped
  - locale_translation_emitted
  - hard_gate_passed
  - hard_gate_failed
  - skill_emitted
  - false_completion_claim
preconditions:
- id: PC-01
  description: zvt package installed and importable
  check_command: python3 -c 'import zvt; print(zvt.__version__)'
  on_fail: 'Run: python3 -m pip install zvt  then re-run: python3 -m zvt.init_dirs to initialize data directories'
  severity: fatal
- id: PC-02
  description: K-data exists for target entities (required before backtesting)
  check_command: python3 -c "from zvt.api.kdata import get_kdata; df = get_kdata(entity_ids=['stock_sh_600000'], limit=1);
    assert df is not None and len(df) > 0, 'No kdata found'"
  on_fail: 'Run recorder first: python3 -m zvt.recorders.em.em_stock_kdata_recorder --entity_ids stock_sh_600000  (replace
    with your target entity IDs)'
  severity: fatal
  applies_to_uc:
  - UC-103
  - UC-104
  - UC-105
- id: PC-03
  description: ZVT data directory initialized (~/.zvt or ZVT_HOME)
  check_command: 'python3 -c "import os; from pathlib import Path; zvt_home = Path(os.environ.get(''ZVT_HOME'', Path.home()
    / ''.zvt'')); assert zvt_home.exists(), f''ZVT home not found: {zvt_home}''"'
  on_fail: 'Run: python3 -m zvt.init_dirs'
  severity: fatal
- id: PC-04
  description: SQLite write permission for ZVT data directory
  check_command: python3 -c "import os, tempfile; from pathlib import Path; zvt_home = Path(os.environ.get('ZVT_HOME', Path.home()
    / '.zvt')); test_f = zvt_home / '.write_test'; test_f.touch(); test_f.unlink()"
  on_fail: 'Check directory permissions: chmod u+w ~/.zvt  or set ZVT_HOME environment variable to a writable location'
  severity: warn
intent_router:
  uc_entries:
  - uc_id: UC-101
    name: Portfolio Management CLI Entry Point
    positive_terms:
    - portfolio management
    - CLI
    - command line
    - tax optimization
    - investment analysis
    data_domain: holding_data
    negative_terms:
    - screening
    - trading strategy
    - ML prediction
    - factor computation
    ambiguity_question: Are you looking to use the command-line interface directly, or do you need to integrate one of these
      modules into your own code?
  - uc_id: UC-102
    name: Related Ticker Grouping Utility
    positive_terms:
    - equivalent tickers
    - related securities
    - commodity grouping
    - ticker equivalence
    - substitutable assets
    data_domain: holding_data
    negative_terms:
    - price prediction
    - trading signals
    - factor analysis
    ambiguity_question: Do you need to group equivalent securities for tax purposes, or are you looking for price/volume analysis
      of individual tickers?
  - uc_id: UC-103
    name: Tax-Optimized Selling Strategy
    positive_terms:
    - minimize gains
    - tax-efficient selling
    - capital gains optimization
    - lot selection
    - cost basis optimization
    data_domain: holding_data
    negative_terms:
    - buy signals
    - screening
    - portfolio rebalancing
    - ML prediction
    ambiguity_question: Are you deciding which lots to sell to minimize taxes, or are you looking for new investment opportunities
      to buy?
  - uc_id: UC-104
    name: Asset Allocation Analysis
    positive_terms:
    - asset allocation
    - portfolio breakdown
    - asset class distribution
    - allocation report
    - portfolio composition
    data_domain: holding_data
    negative_terms:
    - tax loss harvesting
    - screening
    - trading signals
    - ML prediction
    ambiguity_question: Do you need a report showing how your portfolio is allocated across asset types, or are you looking
      for specific tax optimization strategies?
  - uc_id: UC-105
    name: Tax Loss Harvesting Opportunity Detection
    positive_terms:
    - tax loss harvesting
    - loss identification
    - wash sale
    - TLH opportunities
    - tax loss selling
    data_domain: holding_data
    negative_terms:
    - buy signals
    - portfolio allocation
    - screening
    - ML prediction
    ambiguity_question: Are you looking for securities to sell at a loss for tax benefits, or do you need to identify securities
      to buy or allocate differently?
context_state_machine:
  states:
  - id: CA1_MEMORY_CHECKED
    entry: Task started
    exit: All memory queries attempted and recorded; memory_unavailable set if failed
    timeout: 30s — skip memory, mark memory_unavailable=true, proceed to CA2
  - id: CA2_GAPS_FILLED
    entry: CA1 complete
    exit: 'All FATAL-priority required inputs answered: target market (A-share/HK/US), data source, time range, strategy type'
    timeout: NOT skippable — FATAL inputs MUST be user-answered before proceeding
  - id: CA3_PATH_SELECTED
    entry: CA2 complete
    exit: intent_router matched single use case with confidence gap > 20% over next candidate, no data_domain ambiguity
    timeout: Trigger ambiguity_question for top-2 candidates, await user selection
  - id: CA4_EXECUTING
    entry: CA3 complete + user explicit confirmation received
    exit: All hard gates G1-Gn passed and output files written
    timeout: NOT skippable — user confirmation of execution path required
  enforcement: Code generation is PROHIBITED before CA4_EXECUTING. Any regression to earlier state MUST be announced to user.
    buy/sell ordering SL-01 check runs at CA4 entry.
spec_lock_registry:
  semantic_locks:
  - id: SL-01
    description: Execute sell orders before buy orders in every trading cycle
    locked_value: sell() called before buy() in each Trader.run() iteration
    violation_is: fatal
    source_bd_ids:
    - BD-018
  - id: SL-02
    description: Trading signals MUST use next-bar execution (no look-ahead)
    locked_value: due_timestamp = happen_timestamp + level.to_second()
    violation_is: fatal
    source_bd_ids:
    - BD-014
    - BD-025
  - id: SL-03
    description: Entity IDs MUST follow format entity_type_exchange_code
    locked_value: stock_sh_600000 | stockhk_hk_0700 | stockus_nasdaq_AAPL
    violation_is: fatal
    source_bd_ids: []
  - id: SL-04
    description: DataFrame index MUST be MultiIndex (entity_id, timestamp)
    locked_value: df.index.names == ['entity_id', 'timestamp']
    violation_is: fatal
    source_bd_ids: []
  - id: SL-05
    description: 'TradingSignal MUST have EXACTLY ONE of: position_pct, order_money, order_amount'
    locked_value: XOR enforcement in trading/__init__.py:68
    violation_is: fatal
    source_bd_ids: []
  - id: SL-06
    description: 'filter_result column semantics: True=BUY, False=SELL, None/NaN=NO ACTION'
    locked_value: factor.py:475 order_type_flag mapping
    violation_is: fatal
    source_bd_ids: []
  - id: SL-07
    description: Transformer MUST run BEFORE Accumulator in factor pipeline
    locked_value: 'compute_result(): transform at :403 before accumulator at :409'
    violation_is: fatal
    source_bd_ids: []
  - id: SL-08
    description: 'MACD parameters locked: fast=12, slow=26, signal=9'
    locked_value: factors/algorithm.py:30 macd(slow=26, fast=12, n=9)
    violation_is: fatal
    source_bd_ids:
    - BD-036
  - id: SL-09
    description: 'Default transaction costs: buy_cost=0.001, sell_cost=0.001, slippage=0.001'
    locked_value: sim_account.py:25 SimAccountService default costs
    violation_is: warning
    source_bd_ids:
    - BD-029
  - id: SL-10
    description: A-share equity trading is T+1 (no same-day close of buy positions)
    locked_value: sim_account.available_long filters by trading_t
    violation_is: fatal
    source_bd_ids: []
  - id: SL-11
    description: Recorder subclass MUST define provider AND data_schema class attributes
    locked_value: contract/recorder.py:71 Meta; register_schema decorator
    violation_is: fatal
    source_bd_ids: []
  - id: SL-12
    description: Factor result_df MUST contain either 'filter_result' OR 'score_result' column
    locked_value: result_df.columns.intersection({'filter_result', 'score_result'}) non-empty
    violation_is: fatal
    source_bd_ids: []
  implementation_hints:
  - id: IH-01
    hint: 'Use AdjustType enum exactly: qfq (pre-adjust), hfq (post-adjust), bfq (none) — contract/__init__.py:121'
  - id: IH-02
    hint: For A-share kdata, default to hfq for long-term analysis (dividend-adjusted) — trader.py:538 StockTrader
  - id: IH-03
    hint: SQLite connection MUST use check_same_thread=False for multi-threaded recorders
  - id: IH-04
    hint: Accumulator state serialization uses JSON with custom encoder/decoder hooks — contract/base_service.py
  - id: IH-05
    hint: Factor.level MUST match TargetSelector.level (enforced at add_factor) — factors/target_selector.py:84
preservation_manifest:
  required_objects:
    business_decisions_count: 128
    fatal_constraints_count: 54
    non_fatal_constraints_count: 168
    use_cases_count: 5
    semantic_locks_count: 12
    preconditions_count: 4
    evidence_quality_rules_count: 2
    traceback_scenarios_count: 5
architecture:
  pipeline: data_collection -> data_storage -> factor_computation -> target_selection -> trading_execution -> visualization
  stages:
  - id: data_collection
    narrative:
      does_what: TimeSeriesDataRecorder and FixedCycleDataRecorder fetch OHLCV and fundamental data from providers (eastmoney,
        joinquant, baostock, akshare) and persist domain objects (Stock1dKdata, BalanceSheet) to SQLite via df_to_db().
      key_decisions: BD-002 chose evaluate_start_end_size_timestamps for incremental fetch (not full refresh) because comparing
        to get_latest_saved_record avoids redundant API calls; BD-003 chose get_data_map field transformation to keep domain
        schema provider-agnostic.
      common_pitfalls: 'Don''t forget SL-11: Recorder subclass MUST declare both provider and data_schema class attributes
        else initialization fails with assertion error; finance-C-001 fatal violation.'
    business_decisions: []
  - id: data_storage
    narrative:
      does_what: StorageBackend persists DataFrames to per-provider SQLite databases at {data_path}/{provider}/{provider}_{db_name}.db
        using path templates from _get_path_template; Mixin.record_data and Mixin.query_data provide uniform read/write interface.
      key_decisions: BD-004 chose StorageBackend abstraction (not hardcoded SQLite) to allow future cloud storage swap; BD-006
        derives db_name from data_schema __tablename__ for per-domain database isolation.
      common_pitfalls: SL-04 violation (wrong DataFrame index) causes factor pipeline failures downstream; always ensure df.index.names
        == ['entity_id', 'timestamp'] before calling record_data.
    business_decisions: []
  - id: factor_computation
    narrative:
      does_what: Factor.compute() applies Transformer (stateless, e.g. MacdTransformer) then Accumulator (stateful, e.g. MaStatsAccumulator)
        to produce filter_result or score_result columns; EntityStateService persists per-entity rolling state across batches.
      key_decisions: BD-007 chose Factor inheriting DataReader for composable data access; SL-08 locks MACD at (fast=12, slow=26,
        n=9) — chose standard Appel parameters not adaptive because interpretability matters for practitioners.
      common_pitfalls: 'SL-07: Transformer MUST run before Accumulator — swapping order causes NaN propagation; SL-12: result_df
        must contain filter_result OR score_result column or TargetSelector silently drops all signals.'
    business_decisions: []
  - id: target_selection
    narrative:
      does_what: TargetSelector.add_factor() registers Factor instances; get_targets() returns entity_ids passing threshold
        filter at a specific timestamp, enabling point-in-time historical backtesting without look-ahead.
      key_decisions: BD-012 chose registrable factor list (not hardcoded) for runtime customization; BD-013 chose timestamp-specific
        filtering not current-only because backtests need historical point-in-time correctness.
      common_pitfalls: Factor.level MUST match TargetSelector.level (IH-05); mismatched levels cause silent empty target lists
        that look like no signals but are actually level-mismatch bugs.
    business_decisions: []
  - id: trading_execution
    narrative:
      does_what: Trader.run() calls sell() before buy() each cycle, generates TradingSignals with due_timestamp = happen_timestamp
        + level.to_second() for next-bar execution, and applies on_profit_control() for stop-loss/take-profit before regular
        target selection.
      key_decisions: SL-01 locks sell-before-buy order because available_long check in sim_account depends on it — chose this
        over symmetric ordering to prevent implicit leverage; BD-039 chose long=AND/short=OR multi-level logic to reflect
        risk asymmetry.
      common_pitfalls: 'SL-02 violation (immediate execution instead of next-bar) introduces look-ahead bias and makes backtest
        results unreproducible in live trading; SL-10: A-share T+1 constraint — backtesting without it overstates returns.'
    business_decisions: []
  - id: visualization
    narrative:
      does_what: Drawer.draw() combines kline main chart with factor overlays and Rect annotations for entry/exit signals
        using Plotly; Drawable interface on Factor enables consistent chart rendering across data types.
      key_decisions: BD-019 chose drawer_rects subclass override for custom annotations not hardcoded markers — allows traders
        to define entry/exit visuals without modifying base drawing logic.
      common_pitfalls: draw_result=True by default (BD-055) is fine for development but set draw_result=False in production/headless
        environments to avoid Plotly server startup overhead.
    business_decisions: []
  - id: cross_cutting_concerns
    narrative:
      does_what: 'Invariants and utilities that span multiple pipeline stages — collected from 26 source groups: Common Library(3),
        Scaled NAV(3), api_abstraction(17), asset_allocation_by_account(3), asset_allocation_by_class(10), cachedtickerinfo(1),
        and 20 more.'
      key_decisions: 128 BDs merged here because they apply to more than one main stage (e.g. algorithm helpers, default value
        choices, ordering contracts, error handling). Agent should inspect individual BD summaries and link back to affected
        main stages via shared IDs.
      common_pitfalls: Cross-cutting concerns frequently surface as bugs when changes to one main stage unintentionally break
        another. Check constraints referencing these BDs and verify invariants still hold after any stage-local modification.
    business_decisions:
    - id: BD-056
      type: B
      summary: val() function returns 0 for empty Inventory, None for other cases
    - id: BD-057
      type: B/RC
      summary: Decimal type used for each financial calculations
    - id: BD-058
      type: B
      summary: Table footer sums Inventory columns via reduce with currency conversion
    - id: BD-050
      type: B/DK
      summary: Use last 10 days of price data for MF/ETF ratio calculation
    - id: BD-051
      type: B/BA
      summary: Use median ratio instead of mean to avoid extreme values
    - id: BD-052
      type: B/RC
      summary: Warn but don't fail when ETF prices unavailable for MF estimation
    - id: BD-019
      type: M
      summary: Duck-typed API classes, no abstract base
    - id: BD-020
      type: B
      summary: Fava version compatibility via version.parse
    - id: BD-021
      type: B
      summary: Config extracted from Custom directives
    - id: BD-053
      type: B
      summary: CLI mode returns end_date=None (no date filtering)
    - id: BD-054
      type: B
      summary: Config extracted from fava-extension custom directives in beancount file
    - id: BD-055
      type: B
      summary: Version-specific query_func for Fava 1.22+ vs 1.30+ compatibility
    - id: BD-GAP-001
      type: M
      summary: 'Missing: Convergence criteria explicit'
    - id: BD-GAP-002
      type: M
      summary: 'Missing: Matrix ill-conditioning'
    - id: BD-GAP-003
      type: B
      summary: 'Missing: 收益率频率与年化因子'
    - id: BD-GAP-004
      type: B
      summary: 'Missing: 波动率模型族与分布选择'
    - id: BD-GAP-005
      type: B
      summary: 'Missing: 因子 IC 的 demean 与分组对齐'
    - id: BD-GAP-006
      type: RC
      summary: 'Missing: ** "Implement immutable append-only semantics for each data writes with timestamp + hash chaining'
    - id: BD-GAP-007
      type: RC
      summary: 'Missing: ** "Add timezone-aware datetime handling throughout - prefer UTC normalization for each timestamp
        operations'
    - id: BD-GAP-008
      type: M
      summary: 'Missing: 协方差矩阵 PSD 修复策略'
    - id: BD-GAP-009
      type: B
      summary: 'Missing: 协方差估计量选择与收缩'
    - id: BD-GAP-010
      type: B
      summary: 'Missing: VaR/CVaR 置信水平与窗口'
    - id: BD-GAP-011
      type: B
      summary: 'Missing: 波动率模型族与分布选择'
    - id: BD-004
      type: B
      summary: Strategy pattern via pattern_type string lookup
    - id: BD-065
      type: B
      summary: Account allocation uses optional include_children flag for balance rollup
    - id: BD-066
      type: B/BA
      summary: Dynamic dispatch to pattern_type function (by_account_name, by_account_open_metadata)
    - id: BD-001
      type: B
      summary: Metadata-driven bucketing via asset_allocation_* prefix
    - id: BD-002
      type: B
      summary: Single base currency stored in root node
    - id: BD-003
      type: BA
      summary: Unallocated amounts fall through to 'unknown' bucket
    - id: BD-031
      type: B/BA
      summary: Tax adjustment enabled by default in asset allocation
    - id: BD-032
      type: B
      summary: Use first operating currency as base currency for each conversions
    - id: BD-033
      type: B
      summary: Bucket unallocated percentages into 'unknown' bucket when metadata < 100%
    - id: BD-034
      type: B/BA
      summary: Skip negative balances (liabilities) in asset allocation
    - id: BD-035
      type: B
      summary: Remove empty accounts and zero-balance ancestor accounts
    - id: BD-036
      type: B/BA
      summary: Use 'asset_allocation_' metadata prefix for bucket definitions
    - id: BD-037
      type: B
      summary: Convert currencies via operating currencies when direct conversion unavailable
    - id: BD-082
      type: B/BA
      summary: 'Expense ratio conversion: ER * 100 for percentage display'
    - id: BD-005
      type: B/DK
      summary: Cash commodities include operating currencies + metadata-tagged ones
    - id: BD-006
      type: BA
      summary: Empty inventory rows filtered after query
    - id: BD-038
      type: B
      summary: Cash commodities detected via asset_allocation_Bond_Cash metadata = 100
    - id: BD-039
      type: B/BA
      summary: Include operating currencies as cash by default
    - id: BD-040
      type: B/BA
      summary: Default accounts pattern '^Assets' for cash drag detection
    - id: BD-097
      type: B/BA
      summary: loss_threshold defaults to 1 in TLH but 0 in example config (tlh.py:96 vs tlh.py:25)
    - id: BD-103
      type: B/BA
      summary: Asset allocation tree root.currency hardcoded to first operating currency with no fallback
    - id: BD-107
      type: B/BA
      summary: 'INTERACTION: BD-092 × BD-103 → Systemic currency failure when operating_currencies is empty or first currency
        is inappropriate'
    - id: BD-108
      type: RC
      summary: 'INTERACTION: BD-007 × BD-022 × BD-027 × BD-073 → Duplicated 30-day wash sale window hardcoding creates maintenance
        hazard and compliance risk'
    - id: BD-109
      type: BA
      summary: 'INTERACTION: BD-102 × BD-023 × BD-008 → minimizegains has hidden dependency on libtlh leap-year handling for
        tax term classification'
    - id: BD-110
      type: RC
      summary: 'INTERACTION: BD-094 × BD-095 → Hardcoded account filter in 3 modules with single source-of-truth creates concentrated
        failure point'
    - id: BD-111
      type: B/BA
      summary: 'INTERACTION: BD-097 (Contradiction) → loss_threshold defaults to 1 in code but 0 in example config creates
        context-dependent harvesting behavior'
    - id: BD-112
      type: BA
      summary: 'INTERACTION: BD-029 × BD-030 × BD-017 → Cross-account wash sale detection depends on substantially identical
        fund grouping accuracy'
    - id: BD-113
      type: BA
      summary: 'INTERACTION: BD-101 × BD-093 → AccAPI imports Fava internals despite being designed as standalone Beancount
        API'
    - id: BD-114
      type: T
      summary: 'INTERACTION: BD-057 × BD-092 → Decimal precision guarantee depends on single-base-currency assumption holding'
    - id: BD-115
      type: BA/DK
      summary: 'INTERACTION: BD-020 × BD-055 → Fava version compatibility logic duplicated across FavaInvestorAPI with different
        version thresholds'
    - id: BD-116
      type: RC
      summary: 'RISK CASCADE: BD-092 → BD-002 → BD-103 → BD-037 → BD-058 → BD-069 → BD-090 → BD-091 → BD-076 → BD-077'
    - id: BD-117
      type: RC
      summary: 'RISK CASCADE: BD-095 → BD-102 → BD-023 → BD-008 → BD-096 → BD-024'
    - id: BD-098
      type: BA
      summary: ScaledNAV extends RelateTickers inheriting build_commodity_groups() for identicals - both use same metadata
    - id: BD-106
      type: BA
      summary: Each Investor method creates NEW FavaInvestorAPI instance instead of reusing
    - id: BD-092
      type: RC
      summary: Every modules use operating_currencies[0] as single base currency for ALL financial calculations
    - id: BD-094
      type: B/RC
      summary: Account filter 'account_sortkey(account) ~ "^[01]"' hardcoded across TLH, minimizegains, and summarizer
    - id: BD-099
      type: RC
      summary: RelateTickers.substidenticals() combines both 'a__equivalents' AND 'a__substidenticals' by default
    - id: BD-101
      type: DK/B
      summary: AccAPI.root_tree() imports fava/core.Tree despite being in beancountinvestorapi.py
    - id: BD-104
      type: T
      summary: Every modules use 'a__' prefix for auto-generated metadata vs 'asset_allocation_' for user config
    - id: BD-079
      type: B
      summary: 'Portfolio allocation percentage: (balance / total) * 100 rounded to 1 decimal'
    - id: BD-067
      type: B
      summary: 'Asset allocation percentage calculation: (balance / total) * 100'
    - id: BD-068
      type: B/RC
      summary: Recursive subtree balance computation for hierarchical percentages
    - id: BD-069
      type: B/BA
      summary: 'Tax-adjusted position scaling: position * (tax_adj / 100)'
    - id: BD-088
      type: B/RC
      summary: 'Bucket allocation distribution: amount * (meta_value / 100)'
    - id: BD-080
      type: B/BA
      summary: 'Cash drag threshold filter: position >= min_threshold'
    - id: BD-081
      type: B/RC
      summary: Inventory sum via sequential accumulation into single Inventory
    - id: BD-075
      type: B/BA
      summary: Tax burden interpolation between proceeds bracket boundaries
    - id: BD-076
      type: B/RC
      summary: 'Average tax rate calculation: (cumulative_taxes / cumulative_proceeds) * 100'
    - id: BD-077
      type: B/RC
      summary: 'Marginal tax rate: (Δ_taxes / Δ_proceeds) * 100'
    - id: BD-078
      type: B/RC
      summary: Lot selection ordering by estimated tax percentage (ascending)
    - id: BD-089
      type: B/RC
      summary: Decimal rounding for proceeds (0 decimals), tax rates (1-2 decimals)
    - id: BD-091
      type: B/RC
      summary: 'Estimated tax calculation: gain * tax_rate (per term)'
    - id: BD-086
      type: B
      summary: Table sorting with configurable column and direction
    - id: BD-072
      type: B/BA
      summary: Gain term classification using relativedelta for precise date arithmetic
    - id: BD-073
      type: B/RC
      summary: 30-day wash sale lookback period for recent purchases
    - id: BD-074
      type: B/BA
      summary: 'Loss threshold filtering: losses < -loss_threshold'
    - id: BD-090
      type: B/DK
      summary: Market value to basis difference for loss calculation
    - id: BD-014
      type: B
      summary: Metadata prefix filtering for flexible column selection
    - id: BD-015
      type: BA
      summary: Commodity leaf accounts excluded if parent is open
    - id: BD-059
      type: B
      summary: Commodity_leaf accounts only included if parent account has no Open directive
    - id: BD-060
      type: B/RC
      summary: Option to filter summarizer to active commodities only (has positions)
    - id: BD-061
      type: B
      summary: Empty string used for missing column values in summarizer
    - id: BD-011
      type: B/RC
      summary: Lots sorted by estimated tax percentage (ascending)
    - id: BD-012
      type: BA
      summary: Cumulative columns added after sorting
    - id: BD-013
      type: B/BA
      summary: Short-term and long-term tax rates from config
    - id: BD-041
      type: B/BA
      summary: Default short-term and long-term tax rates of 1%
    - id: BD-042
      type: B/RC
      summary: Sort lots by estimated tax percentage ascending
    - id: BD-043
      type: B/BA
      summary: 'Add cumulative columns: cu_proceeds, cu_taxes, tax_avg, tax_marg'
    - id: BD-044
      type: B/RC
      summary: Estimate tax = gain × tax_rate for each lot
    - id: BD-095
      type: BA
      summary: libtlh.get_account_field(options) is the ONLY source of truth for account field extraction, shared by TLH and
        minimizegains
    - id: BD-096
      type: RC
      summary: 'libtlh.get_tables() pipeline order: find_harvestable_lots → harvestable_by_commodity → summarize_tlh → build_recents'
    - id: BD-102
      type: RC
      summary: minimizegains relies on libtlh.gain_term() for short/long term classification
    - id: BD-093
      type: BA
      summary: 'Dual-API pattern: FavaInvestorAPI (Fava context) vs AccAPI (CLI context) implement identical interfaces'
    - id: BD-100
      type: B/BA
      summary: Node tree pattern with underscore-separated naming (e.g., 'equity_domestic') for asset allocation hierarchy
    - id: BD-105
      type: B/DK
      summary: Wash sale detection uses 30-day lookback hardcoded in SQL DATE_ADD(TODAY(), -30)
    - id: BD-083
      type: B/RC
      summary: Union-Find algorithm for building commodity equivalence groups
    - id: BD-084
      type: B
      summary: 'TLH partner inference using symmetric rule: if A→(B,C) then B→(A,C), C→(A,B)'
    - id: BD-085
      type: B/RC
      summary: Representative ticker selection for identical group
    - id: BD-070
      type: B
      summary: MF NAV estimation using median ratio from historical MF/ETF pairs
    - id: BD-071
      type: B/DK
      summary: NAV scaling ratio based on only most recent 10 historical ratios
    - id: BD-087
      type: B
      summary: 'Price ratio calculation: MF_price / ETF_price across matching dates'
    - id: BD-007
      type: B/DK
      summary: 30-day wash sale window hardcoded in query
    - id: BD-008
      type: B/BA
      summary: relativedelta for gain term to handle leap years
    - id: BD-009
      type: B/BA
      summary: Substantially identical tickers read from commodity metadata
    - id: BD-010
      type: B/RC
      summary: Summary aggregates currency values via Decimal sum
    - id: BD-022
      type: B/DK
      summary: 30-day wash sale window for both recent purchases and recent sales
    - id: BD-023
      type: B/BA
      summary: 'Long-term gain threshold: >1 year using relativedelta accounting for leap years'
    - id: BD-024
      type: B/BA
      summary: Default loss_threshold of 1 dollar
    - id: BD-025
      type: B/RC
      summary: Uses a__substidenticals metadata to identify substantially identical securities
    - id: BD-026
      type: B/BA
      summary: Filter accounts via account_sortkey matching ^[01] pattern
    - id: BD-027
      type: B/DK
      summary: Earliest safe sale date = acquisition_date + 31 days
    - id: BD-028
      type: B/RC
      summary: Sort harvestable table by highest to lowest losses
    - id: BD-029
      type: B/RC
      summary: Separate wash_pattern to distinguish taxable accounts from wash-sale accounts
    - id: BD-030
      type: B/DK
      summary: Deduplicate recent purchases by ticker across substantially identical funds
    - id: BD-016
      type: B
      summary: Graph-based inference of TLH partners
    - id: BD-017
      type: B/DK
      summary: Equivalents vs Substidenticals distinction
    - id: BD-018
      type: BA
      summary: Archived tickers filtered from TLH groups
    - id: BD-045
      type: B/RC
      summary: Separate 'a__equivalents' and 'a__substidenticals' metadata fields
    - id: BD-046
      type: B/RC
      summary: Tickers with 'archive' metadata are excluded from TLH calculations
    - id: BD-047
      type: B/RC
      summary: 'TLH partners are made transitive: if A→B, then B→A inferred'
    - id: BD-048
      type: B/RC
      summary: Option to filter TLH partners by fund type (ETF vs MUTUALFUND)
    - id: BD-049
      type: B/RC
      summary: Representative ticker chosen from preferred set (idents_preferred)
    - id: BD-062
      type: B
      summary: Yahoo info cache stored as pickle file in BEAN_ROOT directory
    - id: BD-063
      type: B/BA
      summary: Expense ratio converted from decimal to percentage (×100) on cache write
    - id: BD-064
      type: B/DK
      summary: Remove '-' ISIN values from cached ticker info
resources:
  packages:
  - name: beancount >= 2.3.2
    version_pin: latest
  - name: fava >= 1.26
    version_pin: latest
  - name: beanquery
    version_pin: latest
  - name: Click >= 7.0
    version_pin: latest
  - name: click_aliases >= 1.0.1
    version_pin: latest
  - name: tabulate >= 0.8.9
    version_pin: latest
  - name: packaging >= 20.3
    version_pin: latest
  - name: python_dateutil >= 2.8.1
    version_pin: latest
  - name: yfinance >= 0.1.70
    version_pin: latest
  - name: importlib_metadata >= 1.5.0
    version_pin: latest
  strategy_scaffold:
    entry_point_name: run_backtest
    output_path: result.csv
    execution_mode: backtest
    conditional_entry_points:
      backtest:
        entry_point_name: run_backtest
        output_path: result.csv
      collector:
        entry_point_name: run_collector
        output_path: result.json
      factor:
        entry_point_name: run_factor
        output_path: result.parquet
      training:
        entry_point_name: run_training
        output_path: result.json
      serving:
        entry_point_name: run_server
        output_path: result.json
      research:
        entry_point_name: run_research
        output_path: result.json
    tail_template: "# === DO NOT MODIFY BELOW THIS LINE ===\nif __name__ == \"__main__\":\n    result = run_backtest()  #\
      \ implement above\n    from validate import enforce_validation\n    enforce_validation(result, output_path=\"{workspace}/result.csv\"\
      )\n# === END DO NOT MODIFY ==="
  host_adapter:
    target: openclaw
    timeout_seconds: 1800
    shell_operator_restriction: 'exec tool intercepts && / ; / | — never chain: ''pip install X && python Y''. Use separate
      exec calls.'
    install_recipes:
    - python3 -m pip install beancount >= 2.3.2
    - python3 -m pip install fava >= 1.26
    - python3 -m pip install beanquery
    - python3 -m pip install zvt
    credential_injection: JoinQuant/QMT credentials require user-side '!' prefix shell login. Never hardcode credentials in
      generated scripts.
    path_resolution: '{workspace} resolves to ~/.openclaw/workspace/doramagic at execution time.'
    file_io_tooling: Use openclaw 'write' tool for .py/.sql files; 'exec' tool for python3 /absolute/path/script.py (absolute
      paths only).
constraints:
  fatal:
  - id: finance-C-001
    when: When implementing AccAPI or FavaInvestorAPI query_func
    action: Convert query results to namedtuple format with field names from query types
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Module code expecting attribute access via row.column_name will fail with AttributeError, breaking asset
      allocation, tax loss harvesting, and other financial computations that rely on namedtuple row iteration
    stage_ids:
    - api_abstraction
  - id: finance-C-002
    when: When implementing price loading in AccAPI
    action: Build beancount price map using prices.build_price_map(entries) from each ledger entries
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Multi-currency portfolios will fail to convert positions to base currency, causing asset allocation calculations
      to crash or produce incorrect results when commodities have different denominations
    stage_ids:
    - api_abstraction
  - id: finance-C-003
    when: When parsing Custom directive configurations
    action: Filter entries for Custom type with fava-extension and evaluate value strings using ast.literal_eval
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Configuration parsing will silently return empty dicts for all modules, causing all investor reports to use
      default parameters instead of user-specified configurations, leading to incorrect financial analysis
    stage_ids:
    - api_abstraction
  - id: finance-C-004
    when: When running FavaInvestorAPI with different Fava versions
    action: Use version.parse from packaging library to compare fava_version against 1.22 and 1.30 thresholds
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Query execution will fail with wrong number of arguments error on incompatible Fava versions, breaking the
      Fava web interface entirely
    stage_ids:
    - api_abstraction
  - id: finance-C-006
    when: When running CLI with AccAPI
    action: Pass a valid beancount file path to AccAPI constructor for ledger loading
    severity: fatal
    kind: resource_boundary
    modality: must
    consequence: Ledger loading will fail with FileNotFoundError, preventing any CLI commands from executing; all modules
      (tlh, assetalloc, cashdrag, summarizer) depend on this
    stage_ids:
    - api_abstraction
  - id: finance-C-009
    when: When creating new AccAPI or FavaInvestorAPI implementations
    action: 'Implement each required methods: query_func, build_price_map, get_custom_config, get_commodity_directives, get_operating_currencies,
      realize, root_tree'
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Modules calling missing methods will raise AttributeError at runtime, breaking financial calculations that
      depend on those APIs
    stage_ids:
    - api_abstraction
  - id: finance-C-017
    when: When accessing row data from query_func results
    action: Access row fields using attribute notation (row.column_name) not dictionary notation (row['column_name'])
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: namedtuple rows do not support item access; code using row['column'] will raise TypeError, breaking all module
      queries that expect attribute-style access
    stage_ids:
    - api_abstraction
  - id: finance-C-019
    when: When building commodity groups from incomplete declarations
    action: apply transitive closure to infer complete equivalence/substantially identical groups
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Incomplete relationship inference will cause some substantially identical funds to not be grouped together,
      leading to incorrect wash sale detection and missing TLH partner recommendations
    stage_ids:
    - ticker_relationships
  - id: finance-C-020
    when: When implementing the representative() method
    action: consistently return the same representative ticker for any member of a substantially identical group
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Inconsistent representative selection will cause non-deterministic TLH group calculations, where the same
      ticker gets different partners depending on which group member is used as the key
    stage_ids:
    - ticker_relationships
  - id: finance-C-021
    when: When computing TLH groups from unidirectional declarations
    action: infer bidirectional relationships so if A→B is declared, B→A is also included
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Missing bidirectional inference will cause incomplete TLH partner lists, where only some funds show all their
      valid swap options, leading to suboptimal TLH decisions
    stage_ids:
    - ticker_relationships
  - id: finance-C-025
    when: When relationship_source is extended to read from sources other than commodity directives
    action: maintain compatibility with the existing a__equivalents/a__substidenticals/a__tlh_partners metadata schema
    severity: fatal
    kind: resource_boundary
    modality: must
    consequence: Schema changes will break compatibility with ScaledNAV and other classes extending RelateTickers, causing
      inconsistent ticker relationship data across the system
    stage_ids:
    - ticker_relationships
  - id: finance-C-026
    when: When extending RelateTickers with additional functionality
    action: share the same metadata schema for equivalents and substidenticals declarations
    severity: fatal
    kind: resource_boundary
    modality: must
    consequence: Schema divergence will cause extended classes to miss ticker relationships declared for the base class, resulting
      in incomplete wash sale detection and TLH recommendations
    stage_ids:
    - ticker_relationships
  - id: finance-C-027
    when: When selecting tickers for wash sale comparison
    action: use the representatives of substantially identical groups to prevent within-group comparisons
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Comparing a ticker to itself in its own substantially identical group will falsely trigger wash sale warnings,
      blocking valid TLH transactions
    stage_ids:
    - ticker_relationships
  - id: finance-C-030
    when: When building the wash sale prevention query
    action: use both the current ticker and each its substantially identical partners in the comparison set
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Missing substantially identical tickers from the wash sale query will allow purchases of similar funds within
      30 days, triggering unintended wash sales that disallow the tax loss
    stage_ids:
    - ticker_relationships
  - id: finance-C-031
    when: When implementing substidenticals() for a ticker with no substantially identical partners
    action: return an empty list, not None or a list containing the input ticker
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Returning the input ticker will cause false wash sale warnings when comparing a ticker to itself, incorrectly
      blocking valid TLH transactions
    stage_ids:
    - ticker_relationships
  - id: finance-C-035
    when: When implementing classes that extend RelateTickers
    action: call the parent __init__ or replicate its file loading and database initialization
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Missing database initialization will cause AttributeError when accessing self.db or self.idents, preventing
      the class from functioning
    stage_ids:
    - ticker_relationships
  - id: finance-C-037
    when: When computing asset class percentages for multiple currencies
    action: Convert each positions to a single base currency before aggregating into asset buckets
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Asset allocation percentages will be incorrect if positions in different currencies are summed directly,
      leading to meaningless results like '500 + 750 = 1250 USD' without currency conversion
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-039
    when: When scaling positions for tax adjustment
    action: Verify that positions have a cost spec present after realization before scaling
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Tax-adjusted allocation will be incorrect if positions lose cost information during realization, causing
      wrong basis for tax-adjusted percentage calculations
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-040
    when: When handling multi-currency portfolios with cost currencies different from operating currencies
    action: Include the cost currency in operating_currencies list to enable transitive conversion
    severity: fatal
    kind: resource_boundary
    modality: must
    consequence: 'Currency conversion will fail with ''Error: unable to convert X to base currency Y (Missing price directive?)''
      if cost currency is not available as operating currency or via price chain'
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-052
    when: When writing the asset_allocation function in libaaacc.py
    action: guard against division by zero when portfolio_total is zero
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Division by portfolio_total at line 79 causes ZeroDivisionError when all account balances in selected accounts
      are zero, resulting in complete failure to render any asset allocation report
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-055
    when: When using accapi.cost_or_value in asset allocation calculations
    action: call accapi.cost_or_value with a CLI-based AccAPI instance without first implementing the method
    severity: fatal
    kind: resource_boundary
    modality: must_not
    consequence: AccAPI.cost_or_value is commented out and returns None/not implemented, causing libaaacc.py:71 to fail with
      AttributeError when running via CLI, preventing any asset allocation by account computation
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-060
    when: When implementing asset allocation by account in Fava extension
    action: obtain portfolio data via FavaInvestorAPI which provides cost_or_value functionality
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: FavaInvestorAPI provides the cost_or_value method required for balance calculations (libaaacc.py:71), while
      AccAPI for CLI lacks this; using wrong API class causes AttributeError on cost_or_value call
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-069
    when: When determining which commodities are considered cash
    action: always include operating currencies in the cash commodities list
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Operating currencies like USD will not appear in cash drag output, missing potential cash drag opportunities
      that should be flagged
    stage_ids:
    - cash_drag
  - id: finance-C-079
    when: When implementing loss calculations for tax loss harvesting
    action: Use beancount.core.number.Decimal for each monetary calculations
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Floating-point arithmetic may cause incorrect loss calculations, leading to harvesting the wrong lot quantities
      and potential tax compliance issues
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-080
    when: When determining long-term vs short-term gain classification
    action: Use dateutil.relativedelta to calculate gain term duration
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Incorrect leap year handling can misclassify gains as long-term when they should be short-term (or vice versa),
      resulting in wrong tax rates applied
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-081
    when: When implementing wash sale detection logic
    action: Use a 30-day lookback window and 31-day earliest sale date for wash sale compliance
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Incorrect wash sale window violates IRS wash sale rule, disallowing the loss deduction and potentially triggering
      IRS penalties
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-083
    when: When implementing tax loss harvesting features
    action: Read substantially identical tickers from commodity metadata using a__substidenticals label
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Selling a security and buying a substantially identical one within wash sale window converts harvest into
      a disallowed wash sale
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-094
    when: When implementing tax calculations in gains minimization
    action: Use Decimal type from beancount.core.number for each monetary calculations
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Floating-point float arithmetic introduces rounding errors in tax calculations, potentially causing incorrect
      lot selection and misreported tax liabilities
    stage_ids:
    - minimize_gains
  - id: finance-C-095
    when: When implementing lot sorting in gains minimization
    action: Sort lots by est_tax_percent in ascending order to prioritize highest-loss lots first
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Incorrect sorting order causes suboptimal lot selection, resulting in higher capital gains tax liability
      than necessary
    stage_ids:
    - minimize_gains
  - id: finance-C-096
    when: When classifying tax term (short-term vs long-term) in gains minimization
    action: Use libtlh.gain_term() function with relativedelta for IRS-compliant year boundary calculation
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Using simple year difference instead of relativedelta causes incorrect long/short term classification near
      year boundaries due to leap year handling; IRS defines 'more than 1 year' as requiring 1 year plus at least 1 day
    stage_ids:
    - minimize_gains
  - id: finance-C-101
    when: When applying short-term and long-term tax rates in gains minimization
    action: Separate lots by holding period and apply corresponding st_tax_rate or lt_tax_rate from config
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Wrong tax rate application causes incorrect estimated tax calculations, leading to suboptimal lot selection
      decisions
    stage_ids:
    - minimize_gains
  - id: finance-C-104
    when: When depending on gain_term classification in gains minimization
    action: Replace or modify libtlh.gain_term() with a custom implementation that changes term classification logic
    severity: fatal
    kind: architecture_guardrail
    modality: must_not
    consequence: Custom term classification may violate IRS rules for long/short term gains, causing incorrect tax estimates
      and potential IRS compliance issues
    stage_ids:
    - minimize_gains
  - id: finance-C-128
    when: When implementing any financial calculation across the entire system
    action: Use Decimal from beancount.core.number for each monetary values to preserve exact decimal representation without
      floating-point errors
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Floating-point calculations introduce rounding errors that corrupt PnL, tax estimates, and portfolio value
      calculations, leading to incorrect financial decisions
  - id: finance-C-129
    when: When building any table output consumed by Fava templates or CLI presenters
    action: Return a standardized 4-tuple format (rtypes, rrows, extra, footer) where rrows is a namedtuple with column access
      via row.column_name
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Fava templates and CLI presenters expect namedtuple row objects; breaking this contract causes AttributeError
      in all consumers
  - id: finance-C-130
    when: When implementing the AccAPI interface for data access abstraction
    action: 'Implement each required methods: query_func, realize, root_tree, build_price_map, get_commodity_directives, get_operating_currencies'
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Missing interface methods cause AttributeError when modules attempt data access, breaking both Fava extension
      and CLI
  - id: finance-C-131
    when: When performing any currency conversion or financial aggregation
    action: Use operating_currencies[0] as the single base currency for each financial calculations to collapse multi-currency
      portfolios
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Using currencies inconsistently produces incorrect portfolio totals and asset allocation percentages across
      different currencies
  - id: finance-C-148
    when: When executing the TLH analysis pipeline or refactoring its implementation
    action: 'Maintain strict sequential execution order: find_harvestable_lots → harvestable_by_commodity → summarize_tlh
      → build_recents; do not reorder, parallelize, or cache intermediate outputs between stages as subsequent stages expect
      specifically formatted inputs from predecessors'
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Reordering pipeline stages or using cached outputs from non-sequential execution causes subsequent stages
      to receive incorrectly formatted inputs, producing invalid tax loss harvesting recommendations
    derived_from_bd_id: BD-096
  - id: finance-C-151
    when: When calculating gain term for tax classification using relativedelta
    action: Use relativedelta(years=1) for >1 year threshold calculation to properly handle leap years and varying month lengths;
      must NOT substitute with timedelta(days=365) or simple year subtraction
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Using 365-day timedelta misclassifies assets held exactly 365 days as long-term when they should be short-term,
      and vice versa near year boundaries with leap years, causing incorrect tax rate application
    derived_from_bd_id: BD-023
  - id: finance-C-152
    when: When determining wash sale applicability for security transactions
    action: Distinguish between equivalents (freely interchangeable, no wash sale) and substidenticals (trigger wash sale
      if bought/sold within 61-day window) — do NOT treat each ticker relationships as substantially identical
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Treating equivalents as substantially identical over-flags wash sales, incorrectly preventing legitimate
      fund switches and disallowing valid loss deductions; treating substidenticals as equivalents misses wash sales, causing
      IRS non-compliance
    derived_from_bd_id: BD-017
  - id: finance-C-153
    when: When implementing wash sale detection logic in tax loss harvesting
    action: Enforce the 30-day wash sale window symmetrically on BOTH sides of any transaction — check for overlapping purchases
      within 30 days after sales AND overlapping sales within 30 days before purchases
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Asymmetric wash sale enforcement misses overlapping scenarios; selling at a loss and repurchasing within
      30 days would not trigger wash sale, allowing disallowed loss deductions to pass through undetected
    derived_from_bd_id: BD-022
  - id: finance-C-154
    when: When identifying substantially identical securities for wash sale tracking
    action: Read and respect the a__substidenticals metadata field on securities to identify user-defined substantially identical
      relationships — must NOT rely solely on generic fund matching algorithms
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Skipping the a__substidenticals metadata check misses user-defined substantially identical relationships,
      causing false negative wash sale detection where disallowed losses are not properly flagged for IRS compliance
    derived_from_bd_id: BD-025
  - id: finance-C-156
    when: When classifying gains and losses as short-term or long-term across tax optimization modules
    action: Use libtlh.gain_term() consistently as the authoritative source for short/long term classification in each modules
      including minimizegains — must NOT implement independent classification logic in any module
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Inconsistent classification between TLH and minimizegains creates contradictory tax optimization decisions;
      some modules may harvest losses while others classify the same gains differently, potentially creating IRS compliance
      issues
    derived_from_bd_id: BD-102
  - id: finance-C-157
    when: When implementing wash sale detection logic that references the 30-day window
    action: Centralize the 30-day wash sale constant in a single constants module and import it across each modules (BD-007,
      BD-022, BD-027, BD-073) — must NOT hardcode the 30-day value in multiple locations
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Hardcoding 30-day constant in multiple locations creates maintenance hazard; if IRS rules change or non-US
      jurisdictions need adaptation, missing one location causes inconsistent wash sale detection leading to false positives
      or missed violations
    derived_from_bd_id: BD-108
  - id: finance-C-161
    when: When implementing wash sale detection or determining earliest safe sale dates for tax loss harvesting
    action: Calculate earliest safe sale date as acquisition_date plus exactly 31 days — this boundary is one day beyond the
      30-day IRS wash sale window, ensuring sales occur outside the prohibited period
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Using 30 days instead of 31 days places sales inside the wash sale window, causing the IRS to disallow loss
      deductions and recharacterize gains — this creates tax liability that the backtest does not account for
    derived_from_bd_id: BD-027
  - id: finance-C-163
    when: When configuring wash sale detection for accounts that could trigger wash sale rules
    action: Use separate wash_pattern configurations for taxable accounts versus accounts that could trigger wash sales —
      the wash_pattern for taxable accounts must differ from the wash_pattern for accounts where repurchase would be disallowed
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Using the same wash_pattern for all accounts causes harvesting in one account to incorrectly trigger wash
      sale restrictions in another, disallowing legitimate tax losses across related accounts
    derived_from_bd_id: BD-029
  - id: finance-C-166
    when: When implementing or modifying account filtering logic across TLH, minimizegains, and summarizer modules
    action: Centralize account filter pattern '^[01]' and account field extraction logic in a single shared function — each
      three modules (libtlh.py, libminimizegains.py, libsummarizer.py) must import this function rather than duplicating the
      pattern
    severity: fatal
    kind: architecture_guardrail
    modality: must
    consequence: Hardcoded account filter patterns in three separate modules create concentrated failure points — if the pattern
      needs updating, a missed update in any module causes inconsistent account filtering and contradictory tax optimization
      recommendations
    derived_from_bd_id: BD-110
  - id: finance-C-175
    when: When implementing position scaling with tax adjustment percentage
    action: Validate that tax_adj parameter is non-negative (>= 0) before computing position * (tax_adj / 100); reject or
      clamp negative values to 0
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: A negative tax_adj value produces invalid negative positions, causing strategies to take short positions
      in assets they should only hold long, resulting in completely inverted position logic and potentially unlimited loss
      exposure in margin accounts
    derived_from_bd_id: BD-069
  - id: finance-C-177
    when: When implementing tax-loss harvesting logic that excludes recent purchases
    action: Exclude positions purchased within the last 30 days (including day-of-purchase) from TLH harvesting pool; positions
      with purchase_date >= current_date - 30 days are ineligible
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Failing to exclude recently purchased positions from TLH harvesting causes the system to harvest losses that
      the IRS will disallow under wash sale rules, resulting in unexpected tax liabilities when the disallowed loss is added
      back to cost basis in future years
    derived_from_bd_id: BD-073
  - id: finance-C-192
    when: When implementing any financial calculation (NAV, portfolio valuations, tax lot computations) in any module
    action: Convert each monetary values to operating_currencies[0] before performing calculations — the system enforces using
      the first operating currency as the single canonical base unit; there is no multi-currency fallback
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Mixing currencies without conversion produces mathematically invalid results; adding USD and CNY values without
      conversion creates meaningless numbers that violate accounting consistency and cause incorrect tax calculations
    derived_from_bd_id: BD-092
  - id: finance-C-199
    when: When implementing or modifying libtlh.gain_term() or libtlh.get_account_field() functions
    action: Verify backward compatibility in function signatures and behavior - changes will cascade through both TLH harvestable
      lot identification and minimizegains gain minimization recommendations simultaneously
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: A bug in gain_term leap-year handling or account field parsing silently corrupts both the TLH module's harvestable
      lot identification AND the minimizegains module's gain minimization recommendations, causing suboptimal tax strategy
      recommendations without raising errors
    derived_from_bd_id: BD-095
  - id: finance-C-200
    when: When processing portfolio valuation for accounts with multiple operating_currencies or when operating_currencies
      may be empty
    action: Validate that operating_currencies contains at least one currency before any downstream calculations - if operating_currencies
      is empty, the entire calculation pipeline (BD-103 asset allocation, BD-037 currency conversion, BD-069 position scaling,
      BD-090/091 tax lot calculations, BD-076/077 tax rates) will produce values in invalid currency without raising errors
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Empty operating_currencies causes the entire financial calculation pipeline to produce values in an invalid
      currency, corrupting tax optimization recommendations and asset allocation analysis for multi-currency portfolios -
      the system will show numbers but they represent no valid currency
    derived_from_bd_id: BD-116
  - id: finance-C-201
    when: When implementing minimizegains module or modifying loss_threshold default value
    action: Verify loss_threshold default of $1 is intentional for the tax optimization strategy - changes to this default
      affect both the harvestable lot identification stage and the gain minimization stage of the tax loss harvesting pipeline
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Changing loss_threshold default affects both pipeline stages, potentially causing lots with small losses
      to be excluded from harvesting or different optimization strategies to be recommended, reducing tax savings effectiveness
    derived_from_bd_id: BD-117
  - id: finance-C-202
    when: When implementing minimizegains module or modifying libtlh gain_term() tax term classification
    action: Verify leap year handling in relativedelta calculations for gain_term() - BD-023 and BD-008 verify correct handling,
      changes would cascade through tax lot classification affecting both harvestable lot identification and gain minimization
    severity: fatal
    kind: domain_rule
    modality: must
    consequence: Incorrect leap year handling in gain_term() causes misclassification of short-term vs long-term gains, leading
      to incorrect tax optimization recommendations and potential non-compliance with tax holding period requirements
    derived_from_bd_id: BD-117
  - id: finance-C-207
    when: When implementing wash sale detection logic
    action: Modify the 30-day wash sale lookback period — DATE_ADD(TODAY(), -30) implements IRS IRC Section 1091 regulatory
      requirement; changing this value violates tax compliance
    severity: fatal
    kind: domain_rule
    modality: must_not
    consequence: Modifying the hardcoded 30-day wash sale lookback causes the backtesting system to violate IRS wash sale
      rule IRC Section 1091, potentially allowing loss claims that would be disallowed in actual tax filings and triggering
      IRS penalties
    derived_from_bd_id: BD-105
  regular:
  - id: finance-C-005
    when: When implementing AccAPI root_tree
    action: Import and use fava.core.Tree class with entries to build tree structure
    severity: high
    kind: resource_boundary
    modality: must
    consequence: CLI commands that display account tree structures will fail to import fava.core.Tree, preventing tree-based
      visualizations from working in standalone mode
    stage_ids:
    - api_abstraction
  - id: finance-C-007
    when: When deploying FavaInvestorAPI in web context
    action: Verify Fava version is at least 1.22 to support required query API compatibility
    severity: high
    kind: resource_boundary
    modality: must
    consequence: Fava web interface will fail to execute queries, displaying error messages to users and making all investor
      reports inaccessible via the web UI
    stage_ids:
    - api_abstraction
  - id: finance-C-008
    when: When parsing config with ast.literal_eval
    action: Use Python dict literal syntax in beancount Custom directive values (not JSON)
    severity: high
    kind: resource_boundary
    modality: must
    consequence: ast.literal_eval will raise SyntaxError when parsing JSON format, causing all module configurations to be
      ignored and defaults to be used instead
    stage_ids:
    - api_abstraction
  - id: finance-C-010
    when: When accessing operating currencies in module code
    action: Call accapi.get_operating_currencies() which returns a list, and access first element for single currency operations
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Currency conversion operations will fail with index error or wrong currency when expecting single base currency
      but receiving list of operating currencies
    stage_ids:
    - api_abstraction
  - id: finance-C-011
    when: When instantiating AccAPI in Fava web context
    action: Instantiate AccAPI directly in Fava extension code that runs in web context
    severity: high
    kind: architecture_guardrail
    modality: must_not
    consequence: AccAPI requires a beancount file path which is not available in Fava web context; using AccAPI instead of
      FavaInvestorAPI will cause loader.load_file to fail with FileNotFoundError
    stage_ids:
    - api_abstraction
  - id: finance-C-012
    when: When configuring fava-extension Custom directive
    action: Use 'fava_investor' as part of the config key name for fava_investor module configurations
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: get_custom_config will fail to find module configurations, returning empty dicts and causing all modules
      to use default parameters instead of user-specified settings
    stage_ids:
    - api_abstraction
  - id: finance-C-013
    when: When claiming API functionality
    action: Claim real-time price updates or live trading execution capability
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: API only reads historical beancount ledger entries; no mechanism exists for real-time data or trade execution,
      misleading users about system capabilities
    stage_ids:
    - api_abstraction
  - id: finance-C-014
    when: When presenting query results or calculations
    action: Present backtested calculations as guaranteed future investment outcomes
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Historical ledger analysis does not predict future returns; presenting tax loss harvesting opportunities
      or asset allocation suggestions as guaranteed profits violates financial advisory regulations
    stage_ids:
    - api_abstraction
  - id: finance-C-015
    when: When considering duck-typing as reason to skip interface validation
    action: Skip verifying that new API implementations have each required method signatures
    severity: high
    kind: rationalization_guard
    modality: must_not
    consequence: Missing methods will cause AttributeError at runtime when modules call query_func, build_price_map, or other
      APIs; duck-typing only works when interface contract is fulfilled
    stage_ids:
    - api_abstraction
  - id: finance-C-016
    when: When simplifying API by removing version compatibility branches
    action: Remove Fava version 1.22 compatibility branch assuming no users have older versions
    severity: medium
    kind: rationalization_guard
    modality: must_not
    consequence: Users running Fava 1.22-1.29 will get TypeError when executing queries, breaking the web interface for a
      significant portion of the user base
    stage_ids:
    - api_abstraction
  - id: finance-C-018
    when: When declaring ticker relationships in Beancount commodity directives
    action: use 'a__equivalents' for same-share-class relationships that don't trigger wash sales
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using 'a__substidenticals' for same-share-class funds instead of 'a__equivalents' will incorrectly flag them
      as wash sale risks, leading to missed TLH opportunities and potential false wash sale warnings
    stage_ids:
    - ticker_relationships
  - id: finance-C-022
    when: When declaring archived tickers in commodity directives
    action: include 'archive' in the commodity metadata to mark it as no longer held
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without proper archive marking, archived tickers will continue appearing in active TLH recommendations, suggesting
      swaps to funds that are no longer part of the portfolio
    stage_ids:
    - ticker_relationships
  - id: finance-C-023
    when: When declaring TLH partners in commodity directives
    action: use 'a__tlh_partners' metadata field (not 'tlh_partners') with comma-separated ticker values
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using the old 'tlh_partners' field or incorrect format will result in zero TLH partners being read, eliminating
      all inferred TLH swap recommendations
    stage_ids:
    - ticker_relationships
  - id: finance-C-024
    when: When using RelateTickers with a commodities file path
    action: provide an existing file path or None; exit gracefully if file doesn't exist
    severity: high
    kind: resource_boundary
    modality: must
    consequence: Missing file handling will cause abrupt program termination without clear error message, making debugging
      difficult for users
    stage_ids:
    - ticker_relationships
  - id: finance-C-028
    when: When computing TLH groups in Step 3
    action: apply the bidirectional inference rule exactly once without iteration or convergence
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Iterating to convergence will cause unintended transitive effects where A→B→C creates A→C directly, which
      may not be appropriate for all fund relationships
    stage_ids:
    - ticker_relationships
  - id: finance-C-029
    when: When filtering archived tickers from TLH groups
    action: remove archived tickers from both keys and values in Step 5
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Archived tickers appearing as keys will cause downstream errors; archived tickers in values will recommend
      funds that are no longer held in the portfolio
    stage_ids:
    - ticker_relationships
  - id: finance-C-032
    when: When comparing funds in same_type_funds_only mode
    action: compare quote types using the 'a__quoteType' metadata field with values like 'ETF' or 'MUTUALFUND'
    severity: medium
    kind: architecture_guardrail
    modality: must
    consequence: Without type filtering, mixing ETFs and mutual funds in TLH recommendations may create unsuitable swap suggestions
      that are not truly equivalent investment vehicles
    stage_ids:
    - ticker_relationships
  - id: finance-C-033
    when: When TLH analysis results are presented to users
    action: present the results as tax or financial advice
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Presenting computational results as advice may lead users to make financial decisions without consulting
      qualified tax professionals, potentially resulting in IRS penalties or missed tax optimization opportunities
    stage_ids:
    - ticker_relationships
  - id: finance-C-034
    when: When declaring a__tlh_partners metadata
    action: declare only funds that are NOT substantially identical to the current fund
    severity: high
    kind: domain_rule
    modality: must
    consequence: Including substantially identical funds in TLH partners will produce misleading recommendations since swapping
      between substantially identical funds triggers wash sales rather than achieving tax loss harvesting
    stage_ids:
    - ticker_relationships
  - id: finance-C-036
    when: When using the pretty_sort() function for display
    action: document that the sort produces valid but potentially different results across different runs
    severity: low
    kind: operational_lesson
    modality: should
    consequence: Users expecting deterministic ordering may be confused when the same TLH groups appear in different order
      on different runs, potentially leading to misread reports
    stage_ids:
    - ticker_relationships
  - id: finance-C-038
    when: When bucketing commodities by asset_allocation_* metadata
    action: Verify bucket names use underscores consistently for hierarchical tree construction
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Tree hierarchy will be incorrectly constructed if bucket names contain hyphens or other separators, causing
      'equity-domestic' to be treated as one node instead of nested 'equity' and 'domestic' nodes
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-041
    when: When calculating asset allocation percentages
    action: Use Decimal type for percentage calculations to avoid floating-point rounding errors
    severity: high
    kind: domain_rule
    modality: must
    consequence: Percentage calculations will have rounding errors when using float, potentially causing percentages to not
      sum to 100.00% exactly
    stage_ids:
    - asset_allocation_by_class
    - asset_allocation_by_account
  - id: finance-C-042
    when: When validating asset_allocation_* metadata percentages
    action: Pad remaining percentage to 'unknown' bucket if metadata does not sum to 100%
    severity: medium
    kind: domain_rule
    modality: must
    consequence: Portfolio allocation will not sum to 100% if metadata percentages don't add up, silently misrepresenting
      the actual portfolio allocation
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-043
    when: When building asset allocation tree from buckets
    action: Skip positions with negative balances from asset allocation calculations
    severity: medium
    kind: domain_rule
    modality: must_not
    consequence: Liabilities will be incorrectly included as positive asset allocation, distorting portfolio composition and
      percentages
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-044
    when: When specifying commodities for asset allocation classification
    action: Prefix each asset class metadata keys with 'asset_allocation_'
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Commodities without the asset_allocation_ prefix will not be included in any bucket, causing incomplete portfolio
      representation
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-045
    when: When calculating asset allocation in multi-currency portfolios
    action: Add price entries for each commodities to enable accurate market value conversion
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Portfolio allocation will be incorrect if price entries are missing or outdated, causing inaccurate market
      valuations of held commodities
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-047
    when: When excluding ancestor accounts from asset allocation
    action: Set excluded ancestor account balances to empty Inventory instead of removing them
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Ancestor accounts with explicit transactions will inflate asset allocation if balances are not zeroed, causing
      double-counting of holdings
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-048
    when: When reporting asset allocation results
    action: Claim percentages are accurate for live trading without considering price timing
    severity: medium
    kind: claim_boundary
    modality: must_not
    consequence: Asset allocation percentages depend on price entries at calculation time; presenting these as precise allocations
      ignores that prices change throughout the trading day
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-049
    when: When filtering accounts for asset allocation
    action: Include accounts with zero balance in the allocation calculation
    severity: low
    kind: operational_lesson
    modality: must_not
    consequence: Empty accounts will create unnecessary tree nodes and potentially confuse the hierarchical structure without
      providing meaningful allocation data
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-050
    when: When implementing tax-adjusted positions with multiple currency conversions
    action: Convert each positions to base currency before applying tax adjustment scaling
    severity: high
    kind: domain_rule
    modality: must
    consequence: Tax-adjusted position values will be incorrect if cost currencies are not converted to base currency before
      scaling, causing wrong percentage allocations
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-051
    when: When configuring the asset allocation module
    action: Set skip_tax_adjustment to True only when not using tax-adjusted accounts
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Tax-deferred accounts like retirement accounts will show incorrect allocations if tax adjustment is skipped
      when it should be applied
    stage_ids:
    - asset_allocation_by_class
  - id: finance-C-054
    when: When calculating asset allocation percentages
    action: verify percentages sum to exactly 100% within each portfolio group
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without rounding or normalization logic, individual allocation percentages may not sum to 100%, violating
      the fundamental invariant that total allocation must equal 100% and breaking acceptance criteria
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-056
    when: When extending asset allocation selection strategies
    action: implement new selection strategies as by_* functions in libaaacc.py module scope
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: The pattern_type lookup uses globals()['by_' + pattern_type] to dynamically locate selection strategy functions;
      functions not defined at module level or with incorrect naming will raise KeyError, breaking portfolio grouping
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-057
    when: When configuring asset allocation by account patterns
    action: verify regex patterns match against existing account names in the root tree
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Non-matching regex patterns result in empty selected_accounts list, causing portfolio_total to be zero and
      triggering division by zero error, producing no meaningful allocation output
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-058
    when: When running asset allocation by account via CLI
    action: attempt to execute assetalloc_account CLI which exits with error message
    severity: high
    kind: resource_boundary
    modality: must_not
    consequence: 'assetalloc_account.py:59 contains sys.exit(''Error: CLI not yet implemented''), causing immediate termination
      of CLI execution with no asset allocation output, blocking automated workflows'
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-059
    when: When converting account balances to operating currency
    action: verify that at least one account balance exists in the operating currency before computing percentages
    severity: high
    kind: domain_rule
    modality: must
    consequence: When no accounts have balances in the configured operating currency, rrows stays empty, leading to portfolio_total=0
      and division by zero, producing no allocation output despite valid configuration
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-061
    when: When returning table data for Fava template rendering
    action: format output as (title, table_data) tuple with correct rtypes structure
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Fava template expects (title, table_data) tuple structure from table_list_renderer macro; incorrect tuple
      format causes Jinja2 template rendering failure and empty output
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-062
    when: When using include_children configuration option
    action: call cost_or_value with include_children parameter correctly forwarded to accapi
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: include_children defaults to False in config.get() but is passed to cost_or_value; incorrect handling causes
      child account balances to be excluded when user expects them included, producing incorrect portfolio totals
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-063
    when: When adding new selection strategy types via pattern_type
    action: use unvalidated user input as pattern_type without verifying the corresponding function exists
    severity: high
    kind: domain_rule
    modality: must_not
    consequence: Dynamic function lookup via globals()['by_' + pattern_type] with unvalidated config input allows attacker
      or misconfigured user to trigger KeyError exceptions, potentially exposing internal module structure or causing denial
      of service
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-064
    when: When presenting asset allocation results
    action: claim the percentages represent actual market timing or real-time trading accuracy
    severity: medium
    kind: claim_boundary
    modality: must_not
    consequence: Asset allocation percentages are computed from historical cost basis and may not reflect current market values,
      especially for assets with significant unrealized gains; presenting these as real-time allocation overstates precision
      of actual portfolio composition
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-065
    when: When running asset allocation by account via Beancount CLI
    action: expect the same functionality as the Fava web UI implementation
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: CLI implementation explicitly exits with error and cost_or_value is not implemented for AccAPI; users attempting
      CLI usage will receive immediate termination, not a degraded but functional experience
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-066
    when: When the pattern matches accounts but they have zero balance
    action: handle the empty result set gracefully with informative output
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: When pattern matches accounts but all have zero balance in operating currency, rrows becomes empty, portfolio_total=0,
      and percentage calculation fails silently, producing no output to indicate why allocation is empty
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-067
    when: When using regex patterns with account name matching
    action: compile regex patterns with error handling for invalid regex syntax
    severity: high
    kind: operational_lesson
    modality: must
    consequence: re.compile(pattern) at libaaacc.py:29 and :48 raises re.error for invalid regex patterns, crashing the entire
      asset allocation report and preventing viewing of any portfolio data
    stage_ids:
    - asset_allocation_by_account
  - id: finance-C-068
    when: When building the cash drag analysis table
    action: filter out rows where position equals empty Inventory before displaying
    severity: high
    kind: domain_rule
    modality: must
    consequence: Empty zero-value rows will appear in the table output, cluttering the display with meaningless rows showing
      no cash balances
    stage_ids:
    - cash_drag
  - id: finance-C-070
    when: When filtering cash positions by minimum threshold
    action: apply min_threshold only after filtering empty positions and using converted position value
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Threshold filtering may fail with AttributeError when position inventory contains no positions, or may compare
      wrong currency values
    stage_ids:
    - cash_drag
  - id: finance-C-071
    when: When configuring the cash drag module
    action: set accounts_exclude_pattern to exclude wallet cash and zero-sum accounts from cash drag analysis
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Physical wallet cash (like Cash-In-Wallet) and zero-sum reconciliation accounts will incorrectly appear as
      uninvested cash drag
    stage_ids:
    - cash_drag
  - id: finance-C-072
    when: When running cash drag in command-line mode vs Fava
    action: use AccAPI for CLI and FavaInvestorAPI for Fava extension, both providing consistent query_func interface
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Cash drag analysis will fail entirely when running from command line, only working within Fava web interface
    stage_ids:
    - cash_drag
  - id: finance-C-073
    when: When using get_only_position() on Inventory objects
    action: verify Inventory contains exactly one position before calling get_only_position()
    severity: high
    kind: domain_rule
    modality: must
    consequence: ValueError exception will be raised when get_only_position() is called on multi-position Inventory, crashing
      the cash drag analysis
    stage_ids:
    - cash_drag
  - id: finance-C-074
    when: When presenting cash drag analysis results
    action: display balances converted to the primary operating currency for consistent comparison
    severity: medium
    kind: architecture_guardrail
    modality: must
    consequence: Multi-currency cash holdings cannot be compared or summed meaningfully, making cash drag analysis unreliable
      across different currencies
    stage_ids:
    - cash_drag
  - id: finance-C-075
    when: When identifying cash commodities via metadata
    action: check for metadata_label_cash set to value 100 on commodity declarations
    severity: medium
    kind: domain_rule
    modality: must
    consequence: Money market funds and short-term bonds tagged with asset_allocation_Bond_Cash metadata will not be recognized
      as cash equivalents
    stage_ids:
    - cash_drag
  - id: finance-C-076
    when: When claiming cash drag detection capabilities
    action: claim real-time brokerage balance synchronization or live trading integration
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Users will expect live cash position updates and automated investment capabilities that the system cannot
      provide, leading to unmet expectations
    stage_ids:
    - cash_drag
  - id: finance-C-077
    when: When configuring accounts_pattern for cash drag analysis
    action: use regex pattern anchored to asset account hierarchy (e.g., '^Assets:.*') to avoid false matches
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Income, expense, or liability accounts matching the pattern will incorrectly contribute to cash drag analysis
      with spurious amounts
    stage_ids:
    - cash_drag
  - id: finance-C-078
    when: When accepting user configurations for cash drag module
    action: accept empty string as accounts_exclude_pattern without handling as no-exclusion case
    severity: medium
    kind: resource_boundary
    modality: must_not
    consequence: Empty exclude pattern will cause SQL query to fail or produce incorrect results due to malformed regex in
      WHERE clause
    stage_ids:
    - cash_drag
  - id: finance-C-082
    when: When implementing tax loss harvesting for a client's accounts
    action: Verify that Beancount booking method is set to STRICT (Specific Identification of Shares)
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using average cost method or FIFO/LIFO while claiming SpecID-based TLH violates tax regulations, causing
      disallowed losses and potential audits
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-084
    when: When implementing TLH for users with multiple substantially identical securities
    action: Read substantially identical relationships from both a__substidenticals and a__equivalents commodity metadata
    severity: high
    kind: domain_rule
    modality: must
    consequence: Missing equivalent fund relationships (like VOO/VFINX/VFIAX) causes wash sales when users switch between
      share classes of the same fund
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-085
    when: When deploying tax loss harvesting for a jurisdiction
    action: Claim the tool works for non-US tax jurisdictions without explicit adaptation
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: US-specific rules (SpecID, wash sale 30-day window, >365-day long-term) do not apply to other countries,
      leading to incorrect tax advice
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-086
    when: When presenting tax loss harvesting results to users
    action: Present harvest recommendations without explicit financial/tax advice disclaimer
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Without proper disclaimer, users may treat automated suggestions as professional tax advice, violating regulatory
      requirements and causing financial harm
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-087
    when: When integrating tax loss harvesting into Fava
    action: Apply Fava GUI time filters to the TLH module
    severity: high
    kind: resource_boundary
    modality: must_not
    consequence: Fava time filters cause unpredictable results in TLH since the module uses TODAY() for wash sale calculation
      and needs current market prices
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-088
    when: When handling partial wash sales in tax loss harvesting
    action: Display complex wash sale scenarios that require sophisticated IRS matching rules
    severity: medium
    kind: resource_boundary
    modality: must_not
    consequence: Displaying partial wash sales with ambiguous purchase/sale matching confuses users and may lead to incorrect
      tax filings
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-089
    when: When implementing TLH wash sale detection across accounts
    action: Configure dividend reinvestment to be OFF for each tickers across each accounts
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Dividend reinvestment creates new purchases within the wash sale window, silently invalidating harvest recommendations
      and causing wash sales
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-090
    when: When calculating harvestable losses in TLH summary
    action: Include wash-sale-affected losses in the total harvestable loss summary
    severity: high
    kind: operational_lesson
    modality: must_not
    consequence: Summary includes losses that will be disallowed due to wash sales, overstating actual harvestable tax benefit
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-092
    when: When implementing TLH for non-standard Beancount ledgers
    action: Verify account numbering follows Beancount convention with account_sortkey starting with 0 or 1
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Hardcoded account_sortkey pattern '^[01]' causes non-standard ledgers to fail silently without finding any
      harvestable lots
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-093
    when: When implementing tax loss harvesting queries
    action: Use TODAY() function in SQL queries instead of hardcoded dates
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Hardcoded dates cause look-ahead bias where future information influences current recommendations, and queries
      become stale over time
    stage_ids:
    - tax_loss_harvesting
  - id: finance-C-097
    when: When querying lot data for gains minimization
    action: Verify market_value inventory contains exactly one position using get_only_position()
    severity: high
    kind: domain_rule
    modality: must
    consequence: Inventory with multiple positions causes ambiguous gain calculation and incorrect lot pricing, leading to
      wrong tax estimates
    stage_ids:
    - minimize_gains
  - id: finance-C-098
    when: When calculating cumulative tax columns in gains minimization
    action: Calculate cumulative proceeds, taxes, and gains by iterating through sorted lots in order
    severity: high
    kind: domain_rule
    modality: must
    consequence: Cumulative columns show incorrect incremental tax burden if calculated out of order, breaking the progressive
      selling guidance feature
    stage_ids:
    - minimize_gains
  - id: finance-C-099
    when: When implementing lot selection algorithm in gains minimization
    action: Design lot_selection_algorithm as a replaceable/pluggable component for customization
    severity: medium
    kind: resource_boundary
    modality: must
    consequence: Hardcoded lot selection logic prevents users from implementing jurisdiction-specific or personalized selling
      strategies
    stage_ids:
    - minimize_gains
  - id: finance-C-100
    when: When using the gains minimization results for tax planning
    action: Claim that results account for asset allocation constraints or tax-advantaged account positioning
    severity: high
    kind: resource_boundary
    modality: must_not
    consequence: Results misrepresent tax optimization by ignoring portfolio rebalancing impacts and tax-advantaged account
      considerations, potentially leading to unintended portfolio drift
    stage_ids:
    - minimize_gains
  - id: finance-C-102
    when: When replacing the lot selection algorithm in gains minimization
    action: Maintain the est_tax_percent output column and ascending sort for compatibility with downstream cumulative calculations
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Breaking the est_tax_percent interface breaks cumulative column calculations and interpolation functions
      that rely on sorted lot order
    stage_ids:
    - minimize_gains
  - id: finance-C-103
    when: When accessing configuration in gains minimization
    action: Retrieve minimizegains-specific config via accapi.get_custom_config('minimizegains')
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Accessing config directly bypasses the abstraction layer, causing CLI and Fava plugin interfaces to fail
    stage_ids:
    - minimize_gains
  - id: finance-C-105
    when: When presenting tax burden estimates from gains minimization
    action: Claim results as guaranteed tax savings or accurate tax liability predictions
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Presenting estimates as guarantees misleads users about actual tax outcomes, which depend on individual circumstances,
      tax year changes, and jurisdiction-specific rules
    stage_ids:
    - minimize_gains
  - id: finance-C-106
    when: When using gains minimization for tax planning decisions
    action: Claim that the tool provides wash sale avoidance analysis or considers 30-day rebalancing windows
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Unlike tax_loss_harvesting module, minimizegains does not check for wash sale implications, leading users
      to believe they have comprehensive tax planning when critical rules are missing
    stage_ids:
    - minimize_gains
  - id: finance-C-107
    when: When building lot tables with single positions in gains minimization
    action: Group by cost_date, currency, cost_currency, cost_number to verify each lot is analyzed separately
    severity: high
    kind: domain_rule
    modality: must
    consequence: Incorrect grouping causes mixed lots to be analyzed together, breaking gain calculation per-lot and preventing
      granular tax optimization
    stage_ids:
    - minimize_gains
  - id: finance-C-108
    when: When interpolating tax burden for a specific liquidation amount
    action: Use linear interpolation between cumulative rows when amount falls between two cu_proceeds values
    severity: high
    kind: domain_rule
    modality: must
    consequence: Non-linear interpolation causes incorrect tax burden estimates for specific liquidation amounts, leading
      to suboptimal selling decisions
    stage_ids:
    - minimize_gains
  - id: finance-C-109
    when: When implementing a metadata summarizer config for Beancount
    action: specify directive_type values other than 'accounts' or 'commodities'
    severity: high
    kind: domain_rule
    modality: must_not
    consequence: The build_table function only handles 'accounts' and 'commodities' directive types, and any other value causes
      silent failure with empty table output
    stage_ids:
    - metadata_summarizer
  - id: finance-C-110
    when: When processing commodity leaf accounts in the summarizer
    action: exclude commodity leaf accounts that have an open parent account from output
    severity: high
    kind: domain_rule
    modality: must
    consequence: Commodity accounts (uppercase names) duplicate their parent's metadata in summaries, causing confusing redundant
      rows in the output table
    stage_ids:
    - metadata_summarizer
  - id: finance-C-111
    when: When building table rows from Open directive metadata
    action: fill missing column values with empty strings rather than omitting the row or column
    severity: medium
    kind: domain_rule
    modality: must
    consequence: Tables become misaligned with missing cells or columns omitted entirely, making output unreadable and breaking
      sort operations on the table
    stage_ids:
    - metadata_summarizer
  - id: finance-C-112
    when: When using meta_prefix filtering with specified columns
    action: construct column names by concatenating meta_prefix with each specified column name
    severity: high
    kind: domain_rule
    modality: must
    consequence: Column matching fails and tables display empty values even when matching metadata exists, because the prefix
      is not properly prepended
    stage_ids:
    - metadata_summarizer
  - id: finance-C-113
    when: When using col_labels to rename metadata columns
    action: preserve the order of columns as specified in the columns config array
    severity: high
    kind: domain_rule
    modality: must
    consequence: Column order becomes inconsistent between the header and row data, causing ValueError exceptions when creating
      namedtuples or misaligned table output
    stage_ids:
    - metadata_summarizer
  - id: finance-C-114
    when: When defining column labels in the summarizer config
    action: include spaces in col_labels values
    severity: high
    kind: domain_rule
    modality: must_not
    consequence: Namedtuple field names cannot contain spaces, causing ValueError exceptions when building table output and
      crashing the summarizer
    stage_ids:
    - metadata_summarizer
  - id: finance-C-115
    when: When defining namedtuple field names from config column labels
    action: use column names that start with digits or contain invalid Python identifier characters
    severity: high
    kind: domain_rule
    modality: must_not
    consequence: Namedtuple requires valid Python identifiers as field names, causing ValueError exceptions when column labels
      start with numbers or contain special characters
    stage_ids:
    - metadata_summarizer
  - id: finance-C-116
    when: When executing the active commodities SQL query
    action: use the Beancount convention 'account_sortkey(account) ~ "^[01]"' for filtering investment accounts
    severity: medium
    kind: domain_rule
    modality: must
    consequence: Incorrect account numbering filter causes commodity holdings to be missed from 'active_only' summaries, making
      market_value calculations incomplete or zero
    stage_ids:
    - metadata_summarizer
  - id: finance-C-117
    when: When retrieving the operating currency for balance calculations
    action: access only index [0] of the operating currencies list, assuming at least one currency is defined
    severity: high
    kind: resource_boundary
    modality: must
    consequence: IndexError exception crashes the summarizer when the Beancount file does not define any operating_currency
      option
    stage_ids:
    - metadata_summarizer
  - id: finance-C-118
    when: When compiling the acc_pattern regex in account filtering
    action: provide a valid Python regex pattern string for account matching
    severity: high
    kind: resource_boundary
    modality: must
    consequence: Invalid regex syntax causes re.compile to raise PatternSyntaxError, crashing the summarizer before any table
      generation occurs
    stage_ids:
    - metadata_summarizer
  - id: finance-C-119
    when: When running the summarizer without a fava-extension config
    action: return an empty dictionary from get_custom_config when no config is found
    severity: medium
    kind: resource_boundary
    modality: must
    consequence: Without proper empty dict handling, build_tables iterates over an empty list and produces no output tables,
      silently failing
    stage_ids:
    - metadata_summarizer
  - id: finance-C-120
    when: When implementing metadata prefix filtering with special metadata columns
    action: use either meta_prefix with or without specified_cols, not both modes simultaneously
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Conflicting configuration causes incorrect column selection logic, either returning too many or too few columns
      in the output
    stage_ids:
    - metadata_summarizer
  - id: finance-C-121
    when: When processing closed accounts in metadata summarization
    action: exclude accounts that have a Close directive from the output
    severity: medium
    kind: architecture_guardrail
    modality: must
    consequence: Closed accounts continue appearing in metadata tables, showing stale contact information for accounts no
      longer in use
    stage_ids:
    - metadata_summarizer
  - id: finance-C-122
    when: When adding special 'account' and 'balance' columns to account metadata
    action: conditionally add these columns based on whether they appear in the columns config
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Missing special column handling causes KeyError when accessing row keys, or duplicate columns when 'account'/'balance'
      exist in both metadata and as special additions
    stage_ids:
    - metadata_summarizer
  - id: finance-C-123
    when: When implementing the summarizer module
    action: access data exclusively through the AccAPI/FavaInvestorAPI abstraction, not directly from Beancount internals
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Direct Beancount API access bypasses the abstraction layer, making the code incompatible with Fava CLI mode
      and reducing portability
    stage_ids:
    - metadata_summarizer
  - id: finance-C-124
    when: When requesting the commodity market_value column
    action: enable active_only mode to populate market_value data from current holdings
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Without active_only, market_value column shows empty values for all commodities, making the table misleading
      as it omits actual holding values
    stage_ids:
    - metadata_summarizer
  - id: finance-C-125
    when: When summarizing metadata from Beancount directives
    action: claim support for metadata on transaction postings or other directive types beyond Open directives
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Documentation explicitly states only Open directive metadata is supported; claiming broader support violates
      documented functionality and misleads users
    stage_ids:
    - metadata_summarizer
  - id: finance-C-126
    when: When comparing metadata summarizer output between backtest and live mode
    action: expect identical metadata values if the Beancount ledger entries differ between periods
    severity: low
    kind: claim_boundary
    modality: must_not
    consequence: Metadata comes from Open directive entries; if accounts are opened/closed or metadata values change, the
      summarizer output will differ, making it unsuitable for direct performance comparisons
    stage_ids:
    - metadata_summarizer
  - id: finance-C-132
    when: When generating metadata for auto-computed ticker attributes
    action: Prefix auto-generated metadata labels with 'a__' to distinguish from user-configured 'asset_allocation_' namespace
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Namespace collision causes user配置的资产配置被系统生成的属性覆盖，或者反之，导致资产分类报告错误
  - id: finance-C-133
    when: When presenting or reporting the system's tax optimization capabilities to users
    action: Claim tax advice, financial advice, or investment recommendations — the system is informational only
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Users may make investment decisions based on unverified tax calculations, leading to unexpected tax liability
      and potential IRS penalties
  - id: finance-C-134
    when: When presenting wash sale analysis to users outside the United States
    action: Claim wash sale detection accuracy — the 30-day window is hardcoded for US IRS wash sale rules only
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: International users receive incorrect wash sale warnings based on US-specific rules, causing them to either
      miss legitimate harvesting opportunities or avoid legitimate rebalancing trades
  - id: finance-C-135
    when: When promoting the system to non-Beancount users
    action: Claim compatibility with non-Beancount accounting systems — the entire architecture depends on Beancount's data
      model
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Users attempt to use the system with incompatible ledgers, resulting in complete failure to generate any
      reports
  - id: finance-C-136
    when: When promoting or describing the system's capabilities
    action: Claim real-time trading execution capability — this is a read-only analysis and reporting system with no execution
      interface
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Users expect automated trade execution that does not exist, leading to missed investment opportunities and
      broken workflows
  - id: finance-C-137
    when: When describing tax optimization capabilities to users
    action: Claim tax optimization accuracy for users without US SpecID/STRICT lot booking — the system assumes specific identification
      of shares
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Users on average cost basis accounting receive incorrect harvestable loss calculations, leading to tax filing
      errors or missed deductions
  - id: finance-C-138
    when: When using Fava date filters with the Tax Loss Harvester module
    action: Expect accurate TLH results — Fava date filter selection leads to unpredictable results with the TLH module
    severity: high
    kind: resource_boundary
    modality: must_not
    consequence: Selecting time filters in Fava causes TLH to show incorrect lots, wash sale detection, or summary values
  - id: finance-C-139
    when: When displaying wash sale analysis to users
    action: Claim comprehensive wash sale coverage — partial wash sales and complex matching scenarios are not displayed
    severity: medium
    kind: claim_boundary
    modality: must_not
    consequence: Users rely on incomplete wash sale information and trigger wash sales inadvertently, resulting in disallowed
      loss deductions and IRS adjustments
  - id: finance-C-140
    when: When running gains minimization analysis
    action: Claim asset allocation preservation — the minimizegains algorithm does not account for asset allocation shifts
      caused by selling
    severity: medium
    kind: claim_boundary
    modality: must_not
    consequence: Users following the minimization order inadvertently shift their portfolio allocation, leading to unintended
      risk profile changes
  - id: finance-C-141
    when: When configuring tax rates for gains minimization
    action: Set st_tax_rate and lt_tax_rate to actual applicable tax rates — default value of 1.0 (100%) produces wildly incorrect
      tax estimates
    severity: high
    kind: resource_boundary
    modality: must
    consequence: Default tax rates cause est_tax_percent to be grossly incorrect, leading users to sell wrong lots and overpay
      taxes by up to 100%
  - id: finance-C-142
    when: When implementing asset aggregation or reporting logic that combines values across multiple positions
    action: Verify each values are normalized to the single base currency stored in the root node before aggregation; if base
      currency is missing or values have mixed currencies, fail with an explicit error rather than attempting silent aggregation
    severity: high
    kind: domain_rule
    modality: must
    consequence: Silent mixed-currency aggregation produces nonsensical aggregate percentages and misleading portfolio reports,
      causing investors to make decisions based on fundamentally invalid data
    derived_from_bd_id: BD-002
  - id: finance-C-143
    when: When adding or modifying account selection strategies in the asset allocation by account stage
    action: Implement new strategies as by_* functions that match the pattern_type string from configuration; any new strategy
      requires a corresponding by_* function with the exact string match expected by the config parser
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Strategies without matching by_* functions are silently skipped, causing only the default strategies to run
      and producing incomplete or incorrect account selection results
    derived_from_bd_id: BD-004
  - id: finance-C-144
    when: When implementing wash sale detection logic for tax loss harvesting
    action: Read substantially identical ticker groups from beancount commodity metadata (Custom directives); wash sale applies
      only within user-defined groups marked as substantially identical; cross-group transactions must be treated as distinct
      securities
    severity: high
    kind: domain_rule
    modality: must
    consequence: Incorrectly treating cross-group transactions as substantially identical causes false wash sale disallowances,
      unnecessarily reducing realized losses and increasing tax liability
    derived_from_bd_id: BD-009
  - id: finance-C-145
    when: When using the framework's default tax rate configuration for US 2024 (ST=37%, LT=20%)
    action: Verify that short_term_rate and long_term_rate from configuration match the actual jurisdiction tax brackets;
      if operating outside US 2024, explicitly configure rates via beancount Custom directives before running tax analysis
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Default US tax rates applied to non-US portfolios systematically produce incorrect tax calculations, causing
      either overpayment (if rates too high) or underpayment (if rates too low) with audit exposure
    derived_from_bd_id: BD-013
  - id: finance-C-146
    when: When implementing or modifying wash sale detection queries
    action: Verify the 30-day wash sale window uses 61 total days (30 before + 1 sale day + 30 after); if modifying the window,
      change each related date calculations consistently to maintain the full 61-day look-back/look-forward span
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using inconsistent wash sale windows (e.g., only 30 days total) fails to capture the full IRS 30-day rule,
      potentially reporting wash sale adjustments that are incomplete or missing, risking non-compliance
    derived_from_bd_id: BD-007
  - id: finance-C-147
    when: When implementing tax lot liquidation order in minimize_gains analysis
    action: 'Sort lots by estimated tax percentage in ascending order: negative gains (losses) sorted with most negative first
      for harvesting, positive gains sorted by lowest tax rate first; use this ordering to determine which lots are selected
      for liquidation to minimize immediate tax burden'
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using FIFO or LIFO instead of tax-optimized sorting produces sub-optimal tax outcomes, potentially causing
      investors to pay more taxes than necessary on realized gains
    derived_from_bd_id: BD-011
  - id: finance-C-149
    when: When implementing security substitution logic for NAV calculations or tax lot tracking
    action: Verify both 'a__equivalents' and 'a__substidenticals' metadata fields are combined by default in RelateTickers.substidenticals();
      if using equivalents_only=True, explicitly document that only equivalents are being used and acknowledge reduced substitution
      coverage
    severity: high
    kind: domain_rule
    modality: must
    consequence: Missing either field from the combination causes misclassified securities in NAV calculations and tax lot
      tracking, leading to incorrect portfolio valuations and potential tax reporting errors
    derived_from_bd_id: BD-099
  - id: finance-C-150
    when: When implementing metadata column filtering logic in the summarizer
    action: Preserve prefix-based wildcard matching (e.g., 'contact_' matches 'contact_phone', 'contact_email') for flexible
      metadata column selection
    severity: high
    kind: domain_rule
    modality: must
    consequence: Changing prefix matching to exact matching breaks user-defined flexible schemas; applications relying on
      wildcard metadata lookups will fail to find expected columns
    derived_from_bd_id: BD-014
  - id: finance-C-155
    when: When calculating cumulative tax impact for progressive sell simulations
    action: Compute cumulative columns (running totals of selling amounts and associated taxes) AFTER sorting the lot list,
      not before — ensures cumulative columns reflect the correct progressive tax situation as lots are added in sorted order
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Computing cumulative columns before sorting produces incorrect running totals; users simulating different
      sell quantities see wrong tax impact at each level and may make suboptimal tax decisions
    derived_from_bd_id: BD-012
  - id: finance-C-158
    when: When implementing or refactoring ticker relationship logic for tax loss harvesting
    action: Apply graph-based transitive closure when inferring TLH partner relationships — if ticker A has a TLH partner
      B, and ticker B has a TLH partner C, then tickers A, B, and C must be treated as a single TLH group
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without transitive closure, TLH recommendations may violate wash sale rules when the same underlying position
      is held across multiple tickers in the same group
    derived_from_bd_id: BD-016
  - id: finance-C-159
    when: When configuring or modifying the loss_threshold parameter for tax loss harvesting
    action: Verify that loss_threshold=1 dollar matches the actual transaction cost structure for the account — if per-trade
      costs exceed $1, adjust threshold upward to avoid harvesting trivial losses that generate net negative tax benefit
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Default loss_threshold of $1 may trigger harvests for positions with unrealized losses below transaction
      costs, creating net tax loss after accounting for brokerage fees and spread
    derived_from_bd_id: BD-024
  - id: finance-C-160
    when: When configuring account filters for tax loss harvesting or gain minimization modules
    action: Verify that account_sortkey values for each accounts intended for tax optimization follow the ^[01] regex pattern
      — accounts not matching this pattern are silently excluded from TLH and minimizegains processing
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Misconfigured account sortkey values silently exclude accounts from tax optimization, causing missed harvesting
      opportunities or suboptimal tax recommendations
    derived_from_bd_id: BD-026
  - id: finance-C-162
    when: When processing recent purchases for wash sale detection across substantially identical funds
    action: Deduplicate recent purchases by ticker symbol before wash sale window check — when multiple substantially identical
      funds hold the same underlying position, only one ticker entry per underlying asset should appear in wash sale analysis
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without ticker-level deduplication, wash sale detection produces false positives triggering multiple overlapping
      wash sale windows for the same underlying position across different fund wrappers
    derived_from_bd_id: BD-030
  - id: finance-C-164
    when: When implementing lot selection logic for gain minimization or tax-aware rebalancing
    action: Sort candidate lots by estimated tax percentage in ascending order — lots with lowest tax impact must be prioritized
      for sale before lots with higher tax impact to minimize tax liability
    severity: high
    kind: domain_rule
    modality: must
    consequence: Incorrect lot sort order causes suboptimal tax outcomes where higher-tax-impact lots are sold before lower-tax-impact
      lots, increasing actual tax liability beyond the minimum achievable
    derived_from_bd_id: BD-042
  - id: finance-C-165
    when: When filtering ticker lists for active TLH group analysis and recommendations
    action: Exclude archived tickers from TLH group analysis — archived tickers represent positions no longer held and generating
      sales recommendations for them produces recommendations users cannot act upon
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Including archived tickers in TLH recommendations causes the system to suggest selling positions that no
      longer exist in the portfolio, creating confusion and wasted analysis effort
    derived_from_bd_id: BD-018
  - id: finance-C-167
    when: When persisting data to cache files (e.g., pickle files) in production
    action: Assume immutable append-only semantics with timestamp and hash chaining exist in the framework — the framework
      lacks these capabilities and pickle cache files can be arbitrarily modified
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Without immutable append-only semantics, cache files can be arbitrarily modified which breaks the audit trail
      and compromises data integrity verification in production environments
    derived_from_bd_id: BD-GAP-006
  - id: finance-C-168
    when: When implementing data persistence operations that require audit trail
    action: Implement immutable append-only semantics for each data write operation, including timestamp and hash chaining
      to create verifiable audit trail — use append-only logging with SHA-256 hash of previous entry
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without timestamp and hash chaining, cache modifications cannot be detected or audited, making it impossible
      to verify data integrity or trace unauthorized changes in production
    derived_from_bd_id: BD-GAP-006
  - id: finance-C-169
    when: When processing datetime values throughout the system
    action: Assume consistent timezone handling across each operations — the framework has mixed timezone handling with some
      timestamps having tzinfo and others without
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Mixed timezone handling causes subtle datetime comparison bugs where timestamps with and without timezone
      info are compared incorrectly, leading to incorrect scheduling, reporting, or calculation errors
    derived_from_bd_id: BD-GAP-007
  - id: finance-C-170
    when: When handling timestamps throughout the system
    action: Normalize each datetime operations to UTC and use timezone-aware datetime objects consistently — apply UTC normalization
      at data ingestion and verify each timestamps stored have explicit timezone information
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without UTC normalization, datetime comparisons across different system components produce incorrect results,
      causing wrong order execution times, misplaced transaction records, and incorrect NAV calculations
    derived_from_bd_id: BD-GAP-007
  - id: finance-C-171
    when: When implementing asset allocation calculation logic
    action: Bucket unallocated percentages (when metadata sums to less than 100%) into an 'unknown' category rather than silently
      scaling or rejecting the configuration
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without unknown bucket handling, unallocated percentages are silently misallocated or cause configuration
      rejection, leading to incorrect portfolio allocation calculations and misleading performance attribution
    derived_from_bd_id: BD-033
  - id: finance-C-172
    when: When calculating NAV scaling ratios
    action: Limit the NAV scaling calculation to the most recent 10 historical ratio observations — use sliding window approach
      where window shrinks to available count when fewer than 10 ratios exist; use fallback behavior when zero ratios are
      available
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using more than 10 historical ratios introduces stale data drift into NAV calculations, causing scaled NAV
      values to diverge from current market conditions and leading to incorrect performance measurement
    derived_from_bd_id: BD-071
  - id: finance-C-173
    when: When modifying ScaledNAV or RelateTickers class hierarchy
    action: Verify that any changes to build_commodity_groups() method or metadata format (a__equivalents, a__substidenticals)
      account for impact on both ScaledNAV and RelateTickers classes — use composition over inheritance or explicit interface
      contracts to decouple if changes are frequent
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Tight coupling through inheritance means changes to build_commodity_groups() or metadata format silently
      affect both classes, potentially causing unexpected behavior in scaled NAV calculations or ticker relationship analysis
    derived_from_bd_id: BD-098
  - id: finance-C-174
    when: When using the framework's expense ratio conversion logic for backtesting or display calculations
    action: Verify that expense ratio values are provided in decimal format before the system multiplies by 100; if source
      data is already in percentage format, divide by 100 before caching to avoid inflated values
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: If expense ratios are sourced in percentage format (e.g., 0.5 for 0.5%) but the cache conversion multiplies
      by 100, the stored value becomes 50%, causing strategy cost calculations to underestimate impact by 100x and making
      expense-adjusted returns appear artificially inflated
    derived_from_bd_id: BD-063
  - id: finance-C-176
    when: When implementing hierarchical balance aggregation with recursive subtree computation
    action: Validate account hierarchy for circular parent references before running balance aggregation; implement cycle
      detection or use iterative depth-first traversal with visited tracking
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Circular parent references in the account hierarchy cause infinite recursion during subtree balance computation,
      leading to stack overflow crashes and complete backtesting failure with no partial results returned
    derived_from_bd_id: BD-068
  - id: finance-C-178
    when: When implementing or refactoring gain term classification logic for short-term vs long-term capital gains
    action: Use python-dateutil relativedelta for holding period date arithmetic to verify accurate IRS 1-year-and-a-day boundary
      calculation — do NOT use simple day-counting (365 days) which fails at month boundaries such as Feb 29 to Mar 1
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Using simple 365-day arithmetic incorrectly classifies gains held across month boundaries, causing wrong
      short-term vs long-term gain determination that leads to tax miscalculation and potential IRS compliance issues
    derived_from_bd_id: BD-072
  - id: finance-C-179
    when: When configuring or validating the TLH loss threshold parameter
    action: Set loss_threshold to a negative value — the threshold must be non-negative; negative values would incorrectly
      include profits or zero-change positions as harvestable losses
    severity: high
    kind: operational_lesson
    modality: must_not
    consequence: Negative loss_threshold allows harvesting of positions without actual losses, generating wash sale complications
      and transaction costs without tax benefit
    derived_from_bd_id: BD-074
  - id: finance-C-180
    when: When implementing or refactoring TLH lot filtering logic
    action: Filter candidate lots using 'losses < -loss_threshold' comparison — not 'losses <= -loss_threshold' or 'losses
      < loss_threshold' which alter the qualifying boundary
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Using <= instead of < includes zero-change lots; using positive comparison flips the logic entirely, harvesting
      profits instead of losses — both cause financial harm
    derived_from_bd_id: BD-074
  - id: finance-C-181
    when: When implementing or refactoring tax burden interpolation between bracket boundaries
    action: 'Handle edge cases for proceeds outside bracket range: use floor ratio for proceeds below lowest bracket, use
      ceiling ratio for proceeds above highest bracket, and return zero tax for zero proceeds'
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Omitting boundary handling causes undefined interpolation results for out-of-range proceeds values, producing
      incorrect tax estimates that could lead to underpayment or overpayment
    derived_from_bd_id: BD-075
  - id: finance-C-182
    when: When configuring or validating the cash drag minimum threshold parameter
    action: Set min_threshold to a negative value — the threshold must be non-negative; negative values would incorrectly
      flag profits or unchanged positions as cash drag
    severity: high
    kind: operational_lesson
    modality: must_not
    consequence: Negative threshold causes incorrect flagging of profitable or neutral positions for liquidation, generating
      unnecessary transaction costs and potentially realizing gains that create tax liability
    derived_from_bd_id: BD-080
  - id: finance-C-183
    when: When implementing or refactoring cash drag position filtering logic
    action: Filter positions using 'position >= min_threshold' comparison — positions exactly equal to min_threshold are included,
      not excluded
    severity: medium
    kind: operational_lesson
    modality: must
    consequence: Using > instead of >= excludes borderline positions at exactly the threshold, reducing the pool of harvestable
      lots and potentially missing tax optimization opportunities
    derived_from_bd_id: BD-080
  - id: finance-C-184
    when: When implementing or refactoring average tax rate calculation
    action: Guard against division by zero when cumulative_proceeds equals zero — return zero average rate or raise explicit
      exception; this edge case cannot occur in valid tax scenarios but must be defensively handled
    severity: high
    kind: domain_rule
    modality: must
    consequence: Division by zero crashes the tax calculation module, causing backtest pipeline failure and preventing portfolio
      tax efficiency analysis
    derived_from_bd_id: BD-076
  - id: finance-C-185
    when: When implementing or refactoring marginal tax rate calculation between tax brackets
    action: Detect and handle zero Δ_proceeds between brackets that yields undefined infinite marginal rate — return a sentinel
      value or skip to next valid bracket range
    severity: high
    kind: domain_rule
    modality: must
    consequence: Division by zero from zero Δ_proceeds crashes the tax calculation module, causing backtest pipeline failure
      and preventing lot-selection optimization
    derived_from_bd_id: BD-077
  - id: finance-C-186
    when: When implementing or refactoring TLH lot selection ordering logic
    action: Sort TLH candidate lots in ascending order of estimated tax percentage to prioritize harvesting highest-tax-impact
      losses first — verify stable sort for ties preserving original list order
    severity: high
    kind: domain_rule
    modality: must
    consequence: Sorting in descending order harvests lowest-tax-impact losses first, wasting limited harvestable slots on
      small losses while missing opportunities to harvest larger losses that provide greater tax savings
    derived_from_bd_id: BD-078
  - id: finance-C-187
    when: When implementing risk calculation or option pricing modules in backtesting
    action: Assume the framework handles volatility model family selection and distribution assumption — the framework does
      not implement volatility model families (e.g., GARCH, EWMA, historical volatility) or distribution selection (normal,
      t-distribution, skew-normal)
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Without explicit volatility model and distribution selection, the framework may use inappropriate assumptions
      causing systematic mispricing of risk measures by 10-30% for strategies with option positions or volatility-dependent
      signals
    derived_from_bd_id: BD-GAP-004
  - id: finance-C-188
    when: When calculating factor IC (Information Coefficient) in cross-sectional backtesting
    action: Assume the framework handles factor IC demeaning and group alignment — the framework does not implement cross-sectional
      IC demeaning (subtracting cross-sectional mean) or proper group alignment before IC computation
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Without IC demeaning, cross-sectional mean returns contaminate factor IC calculations, causing 5-15% systematic
      bias in IC estimates and leading to incorrect factor selection decisions
    derived_from_bd_id: BD-GAP-005
  - id: finance-C-189
    when: When implementing a new subclass of FavaInvestorAPI or extending the API layer
    action: 'Implement each required method signatures: get_commodity_value, get_cost_basis, get_open_amounts — duck typing
      provides no compile-time enforcement; missing methods will not be detected until runtime'
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Missing or misspelled method signatures cause runtime AttributeError when the framework invokes expected
      interface methods, breaking financial calculations and potentially corrupting portfolio valuations
    derived_from_bd_id: BD-019
  - id: finance-C-190
    when: When implementing iterative financial calculations or optimization algorithms
    action: Assume the framework handles convergence automatically without explicit criteria — missing convergence criteria
      means no defined stopping point for iterative calculations
    severity: high
    kind: claim_boundary
    modality: must_not
    consequence: Without explicit convergence criteria, iterative calculations may not terminate reliably, causing either
      infinite loops consuming CPU or premature termination with inaccurate results that silently propagate through financial
      computations
    derived_from_bd_id: BD-GAP-001
  - id: finance-C-191
    when: When implementing any iterative calculation or convergence-dependent algorithm in financial modules
    action: 'Define explicit convergence parameters: max_iterations (required), tolerance (required), and a convergence_check
      callable — document these as class attributes or constructor parameters'
    severity: high
    kind: domain_rule
    modality: must
    consequence: Unbounded iterative calculations may run indefinitely in edge cases, causing system hangs or producing inaccurate
      financial results that accumulate without obvious warning
    derived_from_bd_id: BD-GAP-001
  - id: finance-C-193
    when: When classifying assets into portfolio buckets for asset allocation analysis
    action: Only recognize commodities with metadata prefix 'asset_allocation_*' for bucketing — do not hardcode commodity
      names, use implicit patterns, or implement alternative classification logic
    severity: high
    kind: domain_rule
    modality: must
    consequence: Incorrect asset classification changes portfolio allocation percentages, causing misaligned rebalancing decisions
      that over-weight or under-weight asset classes relative to investment policy targets
    derived_from_bd_id: BD-001
  - id: finance-C-194
    when: When calculating gain holding period for tax-loss harvesting or capital gains classification
    action: Use dateutil.relativedelta for date arithmetic in gain_term calculation — relativedelta correctly handles leap
      year edge cases (Feb 29 + 1 year = Feb 28), matching IRS interpretation of 'greater than 1 year'
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Using simple 365-day arithmetic misclassifies gains near leap years (e.g., Feb 29 purchase date), applying
      incorrect tax rates and causing unexpected tax liability when short-term gains are incorrectly taxed at long-term rates
      or vice versa
    derived_from_bd_id: BD-008
  - id: finance-C-195
    when: When implementing or maintaining version-specific Fava compatibility logic
    action: Duplicate version detection logic across different modules (FavaInvestorAPI and other workers) — use a unified
      version detection utility with a single source of truth for version thresholds
    severity: high
    kind: architecture_guardrail
    modality: must_not
    consequence: Duplicated version thresholds create silent inconsistencies where query results differ based on which module's
      version logic is used, causing backtest results to diverge from live trading behavior when Fava releases new versions
    derived_from_bd_id: BD-115
  - id: finance-C-196
    when: When using AccAPI.root_tree() or designing new AccAPI functionality
    action: Import fava.core.Tree in beancountinvestorapi.py — the standalone Beancount API should not depend on Fava internals
      to maintain portability across different Beancount deployments
    severity: medium
    kind: architecture_guardrail
    modality: must_not
    consequence: Fava dependency leak means AccAPI fails when Fava is not installed, preventing use of the 'pure' Beancount
      API in environments without Fava and indicating incomplete separation of concerns that could cause API failures
    derived_from_bd_id: BD-101
  - id: finance-C-197
    when: When implementing or modifying API methods (build_price_map, realize, query_func, get_commodity_directives) in FavaInvestorAPI
      or AccAPI
    action: Verify method signatures and return values remain identical between FavaInvestorAPI and AccAPI implementations
      - any divergence breaks cross-context compatibility and causes consumers using the wrong context to receive incorrect
      data or silent failures
    severity: high
    kind: domain_rule
    modality: must
    consequence: If FavaInvestorAPI and AccAPI implementations diverge on any method (e.g., different parameter handling,
      missing fields, or changed return types), code switching between Fava and CLI contexts will receive inconsistent results
      without explicit errors
    derived_from_bd_id: BD-093
  - id: finance-C-198
    when: When implementing or modifying account field extraction logic in TLH or minimizegains modules
    action: Use libtlh.get_account_field(options) as the sole extraction function - do not duplicate logic or extract similar
      functionality inline; changes to this function affect both TLH and minimizegains simultaneously
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Duplicating account field extraction logic in either TLH or minimizegains creates divergence where one module
      may use stale parsing rules while the other uses updated logic, corrupting tax optimization recommendations
    derived_from_bd_id: BD-095
  - id: finance-C-203
    when: When implementing asset allocation calculations in the asset_allocation_by_class stage
    action: Remove empty accounts (accounts with no holdings) and zero-balance ancestor accounts from asset allocation calculations
      — only include accounts with actual positive balances
    severity: high
    kind: domain_rule
    modality: must
    consequence: Including empty accounts in asset allocation percentages distorts the allocation results, causing portfolio
      displays to show incorrect positions and potentially leading to poor investment decisions based on misleading allocation
      data
    derived_from_bd_id: BD-035
  - id: finance-C-204
    when: When implementing currency conversion logic for multi-currency portfolios in asset_allocation_by_class
    action: Convert currencies via operating currencies as an intermediate step when direct conversion is unavailable — implement
      fallback routing through common currencies (USD, EUR) to verify conversions complete even when direct currency pairs
      lack pricing data
    severity: high
    kind: domain_rule
    modality: must
    consequence: Without currency conversion fallback, multi-currency portfolios with unavailable direct conversion pairs
      will fail to calculate, causing portfolio valuation errors and preventing asset allocation from completing for valid
      international portfolios
    derived_from_bd_id: BD-037
  - id: finance-C-205
    when: When using the framework's default cash detection for cash drag calculations in the cash_drag stage
    action: Verify that operating currencies are included as cash holdings by default — check that base currencies held in
      accounts are captured in cash calculations unless explicitly excluded in configuration
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Default inclusion of operating currencies as cash affects cash drag detection accuracy; strategies relying
      on accurate cash position data may miscalculate idle cash and miss optimization opportunities
    derived_from_bd_id: BD-039
  - id: finance-C-206
    when: When configuring cash drag detection in the cash_drag stage
    action: Verify that the default regex pattern '^Assets' matches the portfolio's actual account naming convention — customize
      the accounts_pattern parameter if the portfolio uses non-standard account naming structures
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Using the default '^Assets' pattern without verification causes cash drag detection to scan incorrect accounts,
      potentially missing cash positions in non-standard account structures and producing incomplete cash drag analysis
    derived_from_bd_id: BD-040
  - id: finance-C-208
    when: When using minimizegains strategy for tax optimization
    action: Preserve the leap-year-aware date arithmetic in libtlh.gain_term() using relativedelta — verify that any refactoring
      maintains the same relativedelta-based date calculations for tax term classification (short-term vs long-term)
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Incorrect leap-year handling in gain_term() corrupts short/long-term tax classification, causing minimizegains
      to make suboptimal liquidation decisions based on wrong holding period calculations and users to pay higher taxes than
      backtested
    derived_from_bd_id: BD-109
  - id: finance-C-209
    when: When configuring cross-account wash sale detection
    action: Verify substantially identical fund classification accuracy (BD-017) before relying on cross-account wash sale
      detection — verify each fund pair is correctly marked as equivalent or substantially identical before using BD-030 deduplication
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Incorrectly marking substantially identical funds as equivalent causes cross-account wash sale detection
      to either miss real violations (false negatives) or trigger false blocks (false positives), leading to either IRS penalties
      or unnecessarily restricted trading
    derived_from_bd_id: BD-112
  - id: finance-C-210
    when: When implementing cash commodity detection logic in asset allocation
    action: Check asset_allocation_Bond_Cash metadata equals exactly 100 to classify an instrument as cash commodity
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using a different threshold or metadata field for cash classification causes incorrect asset allocation,
      potentially leading to inappropriate portfolio rebalancing decisions or misrepresentation of actual cash positions
    derived_from_bd_id: BD-038
  - id: finance-C-211
    when: When implementing API calls for CLI mode batch operations
    action: Set end_date to None in CLI mode to disable date filtering — must not default to current date like GUI mode
    severity: high
    kind: domain_rule
    modality: must_not
    consequence: Defaulting end_date to current date in CLI mode breaks batch operations that need to process all historical
      data, causing incomplete analysis when users run scripts against full historical datasets
    derived_from_bd_id: BD-053
  - id: finance-C-212
    when: When implementing configuration extraction for the investor API
    action: Extract configuration from fava-extension custom directives in the beancount file, not from separate config files
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Using separate config files instead of beancount directives breaks configuration version control and portability,
      causing configuration drift between environments and loss of audit trail
    derived_from_bd_id: BD-054
  - id: finance-C-213
    when: When using the framework's default tax rate parameters for capital gains calculations
    action: Verify that default short_term_tax_rate=1% and long_term_tax_rate=1% match the user's actual tax bracket, adjusting
      if the user falls in higher brackets where actual rates may be 15%, 20%, or 37%
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Default 1% tax rates significantly underestimate tax liability for most investors, causing backtested portfolio
      values to appear materially higher than actual results after tax settlement
    derived_from_bd_id: BD-041
  - id: finance-C-214
    when: When implementing inventory aggregation logic for tax lot tracking
    action: Use sequential accumulation into a single Inventory object, not vectorized operations, to preserve lot-level detail
      for tax calculations
    severity: high
    kind: domain_rule
    modality: must
    consequence: Using vectorized or parallel accumulation loses lot-level granularity, making tax lot tracking incomplete
      and causing incorrect tax reporting for accounts with multiple position lots
    derived_from_bd_id: BD-081
  - id: finance-C-215
    when: When implementing inventory aggregation for large portfolios
    action: Handle numeric type overflow for cumulative inventory sums exceeding standard integer/float bounds - use Decimal
      type or explicit overflow detection
    severity: high
    kind: domain_rule
    modality: must
    consequence: Unchecked overflow in inventory accumulation silently wraps or truncates values, causing portfolio totals
      to become incorrect and triggering wrong tax lot calculations for large positions
    derived_from_bd_id: BD-081
  - id: finance-C-216
    when: When implementing security clustering for commodity-equivalence groups
    action: Use Union-Find data structure with path compression and union by rank for near-constant-time group operations;
      do not replace with dict-based or list-based grouping
    severity: high
    kind: architecture_guardrail
    modality: must
    consequence: Dict-based grouping has O(n) lookup time, causing exponential slowdown when processing portfolios with thousands
      of securities; Union-Find provides near-constant-time operations essential for tax-lot matching
    derived_from_bd_id: BD-083
  - id: finance-C-217
    when: When implementing Union-Find for commodity grouping
    action: 'Handle edge cases: self-unions (union(A,A)) must be idempotent, isolated securities form singleton groups, circular
      dependencies must resolve correctly via algorithm'
    severity: high
    kind: domain_rule
    modality: must
    consequence: Missing edge case handling causes incorrect group membership, leading to wrong commodity-equivalence classification
      and broken tax-lot matching across related securities
    derived_from_bd_id: BD-083
  - id: finance-C-218
    when: When selecting representative tickers from commodity-equivalence groups
    action: Implement deterministic tie-breaking logic for groups with multiple valid candidates; empty groups must produce
      no representative, single-element groups must trivially select that element
    severity: high
    kind: domain_rule
    modality: must
    consequence: Non-deterministic or missing tie-breaking causes inconsistent reporting across runs, making audit trails
      unreliable and potentially causing incorrect tax reporting when different representatives are selected on each execution
    derived_from_bd_id: BD-085
  - id: finance-C-219
    when: When distributing position amounts into allocation buckets
    action: Validate that sum of bucket meta_value weights equals 100 for complete coverage; meta_value of 0 distributes nothing,
      and meta_value > 100 would overallocate and should be rejected
    severity: high
    kind: domain_rule
    modality: must
    consequence: Unvalidated bucket weights causing overallocation or underallocation distorts asset categorization, leading
      to incorrect risk reporting and potentially wrong portfolio allocation decisions
    derived_from_bd_id: BD-088
  - id: finance-C-220
    when: When implementing bucket allocation distribution formula
    action: 'Use the formula: amount * (meta_value / 100); do not reorder or use alternative distribution methods that could
      cause floating-point precision errors with small meta_value percentages'
    severity: medium
    kind: domain_rule
    modality: must
    consequence: Alternative formulas or incorrect operator precedence cause miscalculated bucket distributions that silently
      miscategorize assets, corrupting portfolio analysis reports
    derived_from_bd_id: BD-088
  - id: finance-C-221
    when: When calculating asset allocation percentages
    action: Pre-validate that portfolio total is non-zero before invoking (balance / total) * 100; callers must check total
      > 0 or handle division by zero explicitly
    severity: high
    kind: domain_rule
    modality: must
    consequence: Division by zero on empty portfolios produces Invalid results that propagate to reporting dashboards, causing
      misleading percentage displays and potentially triggering automated rebalancing on zero balances
    derived_from_bd_id: BD-067
  - id: finance-C-222
    when: When implementing TLH partner inference logic in the tax loss harvesting system
    action: 'Apply symmetric closure iteratively until fixed point: if A→(B,C) then B→(A,C) and C→(A,B) must be true; empty
      partner sets must remain empty; large groups may create many inferred edges and require iteration limits'
    severity: high
    kind: domain_rule
    modality: must
    consequence: Incorrect symmetric closure causes wash sale violations to go undetected in complex multi-fund scenarios
      where funds share common holdings; the IRS wash sale rule disallows loss deductions when substantially identical securities
      are purchased within 30 days before or after the sale
    derived_from_bd_id: BD-084
  - id: finance-C-223
    when: When implementing price ratio calculation (MF_price / ETF_price) in scaled NAVs processing
    action: Validate ETF_price is non-zero before division; handle missing ETF price on a date by excluding that observation;
      flag or investigate very high ratios as potential data issues
    severity: high
    kind: domain_rule
    modality: must
    consequence: Division by zero crashes the calculation pipeline; unhandled missing ETF prices create data gaps in NAV estimation;
      very high ratios indicate stale prices or data corruption causing incorrect valuation
    derived_from_bd_id: BD-087
  - id: finance-C-224
    when: When constructing asset allocation tree hierarchies in libassetalloc.py
    action: Verify that the first operating currency used as root node denomination is valid and available; implement fallback
      logic to select an alternative currency (e.g., portfolio base currency, USD) when the first operating currency is empty,
      null, or invalid
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Hardcoded first operating currency with no fallback causes silent failures or crashes when the currency list
      is empty or the first entry is invalid, leading to missing allocation reports and inability to track portfolio performance
    derived_from_bd_id: BD-103
  - id: finance-C-225
    when: When setting up or configuring operating_currencies for portfolio analysis
    action: Validate that operating_currencies is non-empty and first currency is valid before any portfolio analysis; implement
      explicit error handling or default fallback (e.g., USD) if empty
    severity: high
    kind: operational_lesson
    modality: must
    consequence: Empty operating_currencies list causes silent cascading failures across all downstream calculations (TLH,
      cash drag, allocation, minimizegains) that depend on the base currency invariant, with no graceful degradation and no
      user-facing error message
    derived_from_bd_id: BD-107
  - id: finance-C-226
    when: When initializing the TLH module with default loss_threshold parameter
    action: Verify loss_threshold default value (1 in code vs 0 in example config) matches expected behavior; explicitly set
      the value to verify consistent harvesting behavior across initialization paths
    severity: medium
    kind: operational_lesson
    modality: should
    consequence: Inconsistent loss_threshold defaults cause context-dependent TLH behavior — code default=1 filters out penny-level
      losses while config default=0 harvests all losses, leading to different recommendations depending on initialization
      path
    derived_from_bd_id: BD-111
output_validator:
  assertions:
  - id: OV-01
    check_predicate: all(p in inspect.getsource(zvt.factors.algorithm.macd) for p in ['slow=26', 'fast=12', 'n=9'])
    failure_message: 'FATAL: MACD params drifted from (fast=12, slow=26, n=9) — SL-08 violation, non-reproducible signals'
    business_meaning: Standard MACD parameters are a semantic lock; drift makes results incomparable with industry-standard
      indicators and non-reproducible.
    source_ids:
    - SL-08
    - BD-036
  - id: OV-02
    check_predicate: result.get('total_trades', 0) > 0 or result.get('explicit_zero_trade_ack') is True
    failure_message: Zero trades executed — likely missing pre-fetched data (see PC-02) or over-restrictive filters
    business_meaning: A backtest with zero trades is not a valid result; either data is missing or the strategy never triggered.
      Structural non-emptiness check is insufficient — we need business confirmation.
    source_ids:
    - SL-01
    - finance-C-073
  - id: OV-03
    check_predicate: result.get('annual_return') is None or abs(float(result['annual_return'])) <= 5.0
    failure_message: 'FATAL: |annual_return| > 500% — likely look-ahead bias or data error'
    business_meaning: Annual returns exceeding 500% are physically implausible for A-share strategies; indicates look-ahead
      bias or corrupt data.
    source_ids: []
  - id: OV-04
    check_predicate: result.get('holding_change_pct') is None or abs(float(result['holding_change_pct'])) <= 1.0
    failure_message: 'FATAL: |holding_change_pct| > 100% — physically impossible'
    business_meaning: Holding change percentage cannot exceed 100%; violation indicates position accounting error.
    source_ids:
    - BD-029
  - id: OV-05
    check_predicate: result.get('max_drawdown') is None or abs(float(result['max_drawdown'])) <= 1.0
    failure_message: 'FATAL: |max_drawdown| > 100% — impossible for non-leveraged account'
    business_meaning: Maximum drawdown cannot exceed 100% without leverage; violation indicates calculation error or look-ahead
      bias.
    source_ids: []
  - id: OV-06
    check_predicate: not (hasattr(result, 'trade_log') and result.trade_log and any(result.trade_log[i].action == 'sell' and
      i+1 < len(result.trade_log) and result.trade_log[i+1].action == 'buy' and result.trade_log[i].timestamp == result.trade_log[i+1].timestamp
      for i in range(len(result.trade_log)-1)))
    failure_message: 'FATAL: buy-before-sell detected in same cycle — SL-01 violation, creates implicit leverage'
    business_meaning: SL-01 requires sell() before buy() in each cycle; violation means available_long was not updated before
      buying, risking duplicate positions.
    source_ids:
    - SL-01
  scaffold:
    validate_py_path: '{workspace}/validate.py'
    tail_block: "# === DO NOT MODIFY BELOW THIS LINE ===\nif __name__ == \"__main__\":\n    result = run_backtest()\n    from\
      \ validate import enforce_validation\n    enforce_validation(result, output_path=\"{workspace}/result.csv\")\n# ===\
      \ END DO NOT MODIFY ==="
  enforcement_protocol: 1. Never edit validate.py. 2. Never delete the DO NOT MODIFY tail block from the main script. 3. Never
    wrap enforce_validation() in try/except. 4. Never rewrite result write logic — it MUST go through enforce_validation.
    5. If validate.py raises ImportError, fix the dependency, do not remove the call.
acceptance:
  hard_gates:
  - id: G1
    check: '{workspace}/result.csv exists AND file size > 0'
    on_fail: Strategy did not produce output; check run_backtest() return value and enforce_validation() call
  - id: G2
    check: '{workspace}/result.csv.validation_passed marker file exists'
    on_fail: Validation did not complete; review validate.py output and fix assertion failures
  - id: G3
    check: 'Main script contains literal: from validate import enforce_validation'
    on_fail: Validation chain stripped; re-add the import in the DO NOT MODIFY block
  - id: G4
    check: 'Main script contains literal: # === DO NOT MODIFY BELOW THIS LINE ==='
    on_fail: Validation fence removed; regenerate DO NOT MODIFY tail block
  - id: G5
    check: 'result.csv has at least 1 row: pandas.read_csv(result.csv).shape[0] >= 1'
    on_fail: Empty result; check if trade_log is non-empty and factors generated signals. Confirm PC-02 (k-data exists) passed.
  - id: G6
    check: 'If MACD strategy: source contains ''slow=26'' AND ''fast=12'' AND ''n=9'' in algorithm call'
    on_fail: MACD params drifted from SL-08 lock; restore standard (12, 26, 9)
  - id: G7
    check: 'For data pipeline tasks: result.csv contains ''entity_id'' and ''timestamp'' fields'
    on_fail: Missing required columns; check Mixin.query_data return schema and DataFrame MultiIndex reset_index() before
      writing
  - id: G8
    check: 'OV-03 passes: abs(annual_return) <= 5.0 (500%)'
    on_fail: Physical plausibility check failed; investigate look-ahead bias or data corruption in input kdata
  soft_gates:
  - id: SG-01
    rubric: 'Strategy narrative consistency: user intent aligns with generated strategy.py logic. dim_a: signal direction
      (buy/sell) matches intent [1-5, pass>=4]; dim_b: frequency (daily/intraday) aligns [1-5, pass>=4]; dim_c: risk controls
      match user intent [1-5, pass>=4].'
  - id: SG-02
    rubric: 'Factor combination quality. dim_a: no highly correlated factor duplication [1-5, pass>=4]; dim_b: multi-period
      alignment correct [1-5, pass>=4]; dim_c: liquidity filter present for A-share [1-5, pass>=4].'
  - id: SG-03
    rubric: 'Data source selection appropriateness. dim_a: coverage sufficient for target entities [1-5, pass>=4]; dim_b:
      provider latency acceptable for strategy frequency [1-5, pass>=4]; dim_c: no unauthorized provider used without credentials
      [1-5, pass>=4].'
skill_crystallization:
  trigger: all_hard_gates_passed AND user_opt_out_skill_saving != true
  output_path_template: '{workspace}/../skills/{slug}.skill'
  slug_template: '{blueprint_id_short}-{uc_id_lower}'
  captured_fields:
  - name
  - intent_keywords
  - entry_point_script
  - validate_script
  - fatal_constraints
  - spec_locks
  - preconditions
  - install_recipes
  - human_summary_translated
  action: 'After all Hard Gates PASS, resolve slug via slug_template using the executed UC, then write the .skill YAML file
    at output_path_template. Notify user in their detected locale: ''Skill saved as {slug}.skill — next time say one of {sample_triggers}
    from the matched UC to invoke directly.'''
  violation_signal: All hard gates passed but no .skill file exists at expected path
  skill_file_schema:
    name: finance-bp-078 / Portfolio Management CLI Entry Point
    version: v5.3
    intent_keywords:
    - portfolio management
    - CLI
    - command line
    - tax optimization
    - investment analysis
    entry_point: run_backtest
    fatal_guards:
    - SL-01
    - SL-02
    - SL-03
    - SL-04
    - SL-05
    - SL-06
    - SL-07
    - SL-08
    - SL-10
    - SL-11
    - SL-12
    spec_locks:
    - SL-01
    - SL-02
    - SL-03
    - SL-04
    - SL-05
    - SL-06
    - SL-07
    - SL-08
    - SL-09
    - SL-10
    - SL-11
    - SL-12
    preconditions:
    - PC-01
    - PC-02
    - PC-03
    - PC-04
post_install_notice:
  trigger: skill_installation_complete
  message_template:
    positioning: I help you build quant strategies on A-share with ZVT — from data fetch to backtest, one flow.
    capability_catalog:
      group_strategy:
        source: auto_grouped
        strategy_reason: auto-grouped by UC.type (3 distinct values, balanced distribution)
      groups:
      - group_id: live_trading
        name: Live Trading
        description: ''
        emoji: 📦
        uc_count: 3
        ucs:
        - uc_id: UC-101
          name: Portfolio Management CLI Entry Point
          short_description: Provides a unified command-line interface for portfolio management operations including tax loss
            harvesting, asset allocation analysis, cash drag dete
          sample_triggers:
          - portfolio management
          - CLI
          - command line
        - uc_id: UC-103
          name: Tax-Optimized Selling Strategy
          short_description: Determines optimal sell order for securities to minimize realized capital gains by analyzing
            cost basis and holding periods across multiple lots
          sample_triggers:
          - minimize gains
          - tax-efficient selling
          - capital gains optimization
        - uc_id: UC-105
          name: Tax Loss Harvesting Opportunity Detection
          short_description: Identifies securities with unrealized losses that can be sold to harvest tax losses, typically
            looking back 30 days to find positions eligible for was
          sample_triggers:
          - tax loss harvesting
          - loss identification
          - wash sale
      - group_id: data_pipeline
        name: Data Pipeline
        description: ''
        emoji: 📊
        uc_count: 1
        ucs:
        - uc_id: UC-102
          name: Related Ticker Grouping Utility
          short_description: Identifies and groups equivalent or substitutable securities (e.g., VTI, VTSAX, VTSMX) based
            on metadata annotations to support tax lot management and
          sample_triggers:
          - equivalent tickers
          - related securities
          - commodity grouping
      - group_id: reporting
        name: Reporting
        description: ''
        emoji: 📋
        uc_count: 1
        ucs:
        - uc_id: UC-104
          name: Asset Allocation Analysis
          short_description: 'Calculates and reports portfolio allocation breakdown by asset type (stocks, bonds, cash, etc.)
            with percentage distributions from investment account '
          sample_triggers:
          - asset allocation
          - portfolio breakdown
          - asset class distribution
    call_to_action: Tell me which one you want to try.
    featured_entries:
    - uc_id: UC-101
      beginner_prompt: Try portfolio management cli entry point
      auto_selected: true
    - uc_id: UC-102
      beginner_prompt: Try related ticker grouping utility
      auto_selected: true
    - uc_id: UC-103
      beginner_prompt: Try tax-optimized selling strategy
      auto_selected: true
    more_info_hint: Ask me 'what else can you do?' to see all 5 capabilities.
  locale_rendering:
    instruction: On skill_installation_complete, translate ALL user-facing strings (positioning + capability_catalog.groups[].name
      + capability_catalog.groups[].description + capability_catalog.groups[].ucs[].short_description + call_to_action + featured_entries[].beginner_prompt
      + more_info_hint) into detected user locale per locale_contract. Preserve UC-IDs, group_id, emoji, and sample_triggers
      verbatim.
    preserve_verbatim:
    - UC-IDs
    - group_id
    - emoji
    - sample_triggers
    - technical_class_names
  enforcement:
    action: 'Host agent MUST send composed message to user as the FIRST user-facing response after skill_installation_complete
      event. Message MUST contain: positioning, capability_catalog (rendered as markdown tables per group), 3 featured_entries,
      call_to_action, and more_info_hint.'
    violation_code: PIN-01
    violation_signal: First user-facing message post-install does not contain the full capability_catalog (all UCs grouped)
      OR skips featured_entries OR skips call_to_action.
human_summary:
  persona: Doraemon
  what_i_can_do:
    tagline: 'I help you build quant strategies on A-share with ZVT — from data fetch to backtest, one flow. Just tell me
      what you want; I''ll write the code, you don''t have to dig docs. (Heads up: ZVT natively supports A-share, HK, and
      crypto. US stocks — stockus_nasdaq_AAPL — are half-baked; don''t bother for serious work.)'
    use_cases:
    - Tax-Optimized Selling Strategy
    - Related Ticker Grouping Utility
    - Portfolio Management CLI Entry Point
    - A-share MACD daily golden-cross backtest with hfq price adjustment from eastmoney
    - 'End-to-end ZVT pipeline: FinanceRecorder + GoodCompanyFactor + StockTrader'
    - Multi-factor strategy with TargetSelector (AND mode) combining MACD + volume breakout
    - Index composition data collection (SZ1000, SZ2000) with EM recorder
  what_i_auto_fetch:
  - ZVT stage pipeline structure (data_collection → visualization) from LATEST.yaml
  - Semantic locks (SL-01 through SL-12) — especially sell-before-buy ordering and MACD params
  - Fatal constraints (finance-C-*) relevant to your target strategy type
  - 'Default parameters: MACD(12,26,9), hfq adjustment, buy_cost=0.001, base_capital=1M CNY'
  - Entity ID format (stock_sh_600000) and DataFrame MultiIndex convention
  - Provider-specific recorder class names and required class attributes
  what_i_ask_you:
  - 'Target market: A-share (default), HK, or crypto? (US stocks in ZVT are half-baked — stockus_nasdaq_AAPL exists but coverage
    is thin)'
  - 'Data source / provider: eastmoney (free, no account), joinquant (account+paid), baostock (free, good history), akshare,
    or qmt (broker)?'
  - 'Strategy type: MACD golden-cross, MA crossover, volume breakout, fundamental screen, or custom factor?'
  - 'Time range: start_timestamp and end_timestamp for backtest period'
  - 'Target entity IDs: specific stocks (stock_sh_600000) or index components (SZ1000)?'
  locale_rendering:
    instruction: On first user contact, translate all fields above into detected user locale while preserving Doraemon persona
      (direct, frank, mildly snarky, knows limits).
    preserve_verbatim:
    - BD-IDs
    - SL-IDs
    - UC-IDs
    - finance-C-IDs
    - class_names
    - function_names
    - file_paths
    - numeric_thresholds
