Skip to content

Key Decisions

This file is the authoritative log of major architectural decisions for this project. Each entry is sourced from docs/activity_report/ — the validated, consolidated source for project history.

How to read this log: Entries are ordered chronologically (oldest first). Status current means the decision is active. Status superseded means a newer decision replaced it (the old entry is preserved for audit history). When a decision is reversed, add Superseded by: [new decision name] and update status to superseded.


Decision: Choose Playwright + TypeScript for E2E Automation

Status: current
Date: 2026-04-30
Source: apr-26_e2e-framework-choice-rationale.md

Context

The project needed an E2E automation framework that balanced short-term technical convenience with long-term product reliability, skill development aligned with the Canadian QA job market, and structured Page Object Model architecture. A Python-only choice would have been locally simpler but missed broader objectives.

Details

Resolution: Adopted Playwright with TypeScript for the E2E layer. A deliberate split: Python for backend/data/ETL, Playwright + TypeScript for UI automation and browser-side reliability.

Impact: Clear separation of concerns between the Python backend layer and the TypeScript E2E layer. Modern QA skills in both stacks. Multi-browser validation available from day one.

Reversal conditions: Reconsider if the scope shifts to maximum immediate simplicity, if TypeScript/modern QA skills lose job-market relevance, or if multi-browser and structured E2E reporting become unnecessary.


Decision: Implement Four-Mode Quota Policy with Automatic Enrichment Blocking

Status: current
Date: 2026-03-31
Source: mar-26_quota-import-policy.md

Context

The ETL pipeline consumed Marketstack API requests without throttling against a 10,000 request/month limit, risking budget depletion. Retry logic was indiscriminate — re-submitting already-enriched tickers. The Marketstack account endpoint (/v1/account) does not exist in production, leaving operators without real-time quota visibility.

Details

Resolution: Implemented a four-mode quota classification system — normal (<65%), eco (65–79%), protection (80–94%), blocked (≥95%) — that automatically adjusts enrichment behavior and blocks the pipeline entirely at critical thresholds. A local JSON counter at logs/marketstack_quota_usage.json tracks quota usage as fallback. Targeted retry only re-submits tickers missing prices.

Impact: Pipeline operates within monthly quota envelope automatically. Thresholds are operator-configurable via config.ini without code changes. Quota transitions and alerts are logged as structured JSON events.

Reversal conditions: Reconsider if Marketstack significantly increases the monthly quota, if a real account endpoint becomes available, or if API cost structure changes enough to make quota limits non-critical.


Decision: Harden SMTP Alert Delivery with Explicit Transport Mode and Configurable Timeout

Status: current
Date: 2026-03-29
Source: mar-26_smtp-alert-hardening.md

Context

Email alerts existed in the ETL pipeline but lacked hardening: no explicit SMTP transport mode, no bounded timeout, no structured diagnostics. The system needed fail-open behavior (email failures must not block business processing) while improving observability.

Details

Resolution: Added SMTP configuration with explicit transport mode selection (starttls, ssl, none), configurable timeout (SMTP_TIMEOUT_SECONDS, default 10s), and structured event logging for failures and successes. Configuration is environment-variable-only. Fail-open behavior preserved.

Impact: Operators have explicit runtime controls and clearer failure diagnostics. Production-validated 2026-03-29 end-to-end. Pipeline module coverage at 87%.

Reversal conditions: Reconsider if a managed email service (SendGrid, AWS SES) becomes available and acceptable, or if fail-open behavior conflicts with future business requirements.


Decision: Automate GitHub Issue Creation from Repository Context via CLI

Status: current
Date: 2026-03-31
Source: mar-26_github-issue-automation.md

Context

GitHub issues were created manually and inconsistently, especially for non-technical operational workflows. There was no standardized, repeatable way to create issues directly from repository context without GitHub's web interface.

Details

Resolution: Built a CLI automation stack — scripts/create_github_issue.py + src/github_issue_automation.py — that detects the repository from origin, accepts normalized payloads (title, body, labels), provides explicit error messages for missing tokens or unsupported remotes, and includes a dry-run mode for safe validation.

Impact: Issue creation is a single command-line operation. Workflow is repeatable, auditable, and integration-ready. First use: ticket #146 itself validated the end-to-end chain.

Reversal conditions: Reconsider if GitHub's API becomes unavailable, authentication mechanisms change significantly, or the project migrates to a different issue tracker (GitLab, Jira).


Decision: Implement 180-Day Log Rotation and AI Scope Rules Before LLM Wiki Launch

Status: current
Date: 2026-05-05
Source: may-26_ai-token-noise-cleanup-prior-to-llm-wiki.md

Context

Before implementing the LLM Wiki, excessive operational noise (test artifacts, logs, temporary files) was inflating AI token consumption without adding knowledge value. The AI scanned run artifacts instead of business documentation, increasing cost, latency, and response relevance risk. A clean, governed foundation was a prerequisite for an effective wiki.

Details

Resolution: Implemented automated 180-day log rotation (scripts/rotate_logs.py): retains active entries from last 180 days, archives expired entries as .gz in logs/archives/, purges archives after 180 days. Formalized AI scope rules in .github/agents/copilot-instructions.md: prioritize English project knowledge sources, explicitly exclude generated artifacts, temporary files, and operational logs. Locked in place via tests/test_copilot_scope_rule.py.

Impact: Token consumption reduced by eliminating low-value files from AI context. LLM Wiki has a clean, governed foundation. Cleanup industrialized via GitHub Actions scheduled workflow. 123 tests passing.

Reversal conditions: Reconsider if semantic indexing or dynamic context prioritization makes file-based exclusions unnecessary, or if the project adopts a different knowledge management strategy.


Decision: Integrate LangChain as Analytical Complement to Spec Kit — Advisory Layer Only

Status: current
Date: 2026-06-16
Source: jun-26_agentic-sdlc-brainstorm.md

Context

Augmented Investor's long-term vision is to serve as a concrete SDLC automation laboratory. The initial plan called for a full multi-agent hierarchy (CEO + VP team) built with LangChain. Before implementing, a structured review identified four risks: duplication with existing Spec Kit agents, over-automation of sensitive operations (deployment, rollback), orchestration debt from too many agents, and governance dilution if LLMs made critical decisions autonomously.

Details

Resolution: LangChain is introduced as a strictly complementary analytical layer, not as a replacement or competitor to Spec Kit. The boundary is firm:

  • Spec Kit owns the delivery pipeline: specification, planning, task decomposition, guided implementation.
  • LangChain owns advisory-only analytical roles: incident analysis, retrospective synthesis.
  • GitHub Actions + Pytest remain the deterministic release authority (pass/fail gate, no LLM override).
  • Human-in-the-loop is mandatory for all sensitive write operations.

Phase 1 scope is limited to two agents: an Incident Analyzer (log/trace analysis, remediation hypotheses) and a Retrospective Synthesizer (activity report and lessons learned drafts). Autonomous deployment and rollback by LLMs are explicitly excluded at this stage.

Impact: Clear responsibility separation across three layers prevents orchestration conflicts and preserves existing quality and governance guarantees. Complexity grows only with proven value.

Reversal conditions: Reconsider scope expansion only after Phase 1 agents demonstrate stable, measurable value over at least 30 days in production advisory use. Revisit autonomous action boundaries only with explicit governance amendment.


Decision: Surface Management-Rule Indicators as First-Class Portfolio Decision Signals

Status: current
Date: 2026-07-26
Source: jul-26_add-management-rules-indicators.md

Context

The investment strategy already existed, but the dashboard did not expose the two most important decision signals in a direct, action-oriented form. This created friction for day-to-day arbitration and reduced the quality of context available for future advisory AI behavior.

Details

Resolution: Introduced two explicit management-rule indicators on the dashboard (gains to secure, titles to reinforce or liquidate) and a per-stock gauge on the detail page to show distance to the next decision milestone.

Impact: Decision-critical information is visible at a glance, directly where decisions are made. The strategy is now formalized in machine-readable UI signals, improving consistency for both human and AI-assisted analysis.

Reversal conditions: Reconsider if strategy thresholds materially change, if the decision model moves to multi-factor scoring beyond threshold logic, or if a future advisory layer requires a different signal abstraction.


Decision: Add Analyst-Expectation Divergence Indicators and Stock-Level Analyst Target Gauge

Status: current
Date: 2026-07-26
Source: jul-26_add-analyst-signals-indicators.md

Context

After surfacing management-rule indicators, the next gap was market-expectation visibility. The product needed to quickly expose where live prices diverge from analyst target ranges, including contrary cases where market sentiment and price behavior move in opposite directions.

Details

Resolution: Added three analyst-driven dashboard indicators (near high target, near low target, contrary to expectations) and an analyst-target gauge on the stock detail page.

Impact: The dashboard now highlights valuation divergence and counter-intuitive signals early, while stock detail pages support immediate interpretation of current price position within analyst ranges.

Reversal conditions: Reconsider if analyst-source quality degrades, if target semantics change, or if a unified ranking model replaces threshold-based divergence signals.


Decision: Separate Analyst Targets from the 52-Week Range on the Stock Detail Page

Status: current
Date: 2026-08-28
Source: ../../activity_report/aug-26_52week-gauge-add-&-analyst_target-gauge-fix.md

Context

A bug report showed that the stock detail page reused the 52-week low/high values for the analyst-target gauge when the active CSV row had no analyst targets. In real input files, missing analyst values are valid and should remain unavailable rather than being silently replaced by another metric family.

Details

Resolution: The detail page now resolves analyst values strictly from the active CSV (lowest_price_target / highest_price_target) and keeps the 52-Week Range section on a separate gauge using an_bas / an_haut. When analyst targets are missing, the UI shows the unavailable state instead of silently falling back to 52-week thresholds. A dedicated regression set (tc-hold06 to tc-hold10) locks this behavior.

Impact: The product no longer mixes two distinct data families. It is clearer to decision-makers, and the regression suite now guarantees that missing analyst data stays missing instead of being replaced by unrelated historical thresholds.

Reversal conditions: Reconsider if the data contract changes so that analyst target data is stored in the database instead of the CSV, or if a new source is introduced that explicitly owns both target families.


Decision: Surface P/E and PEG Valuation Extremes as First-Class Dashboard Signals

Status: current
Date: 2026-07-08
Source: jul-26_add-pe&peg-valuation-indicators.md

Context

After adding management-rule and analyst-divergence signals, valuation visibility was still incomplete. The dashboard lacked a direct way to identify the most expensive and least expensive holdings through complementary valuation lenses (P/E and PEG) in one glance, creating manual review overhead and weaker context for future advisory AI.

Details

Resolution: Added four explicit valuation cards on /dashboard and /demo: top 10 highest P/E, top 10 lowest P/E, top 10 highest PEG, and top 10 lowest PEG. Inclusion is restricted to numeric, strictly positive values for each ratio. Ordering is deterministic: primary by ratio value (descending or ascending depending on card), secondary tie-break by ticker A-Z.

Impact: Valuation extremes are now visible immediately, without manual sorting. Decision quality improves by combining P/E and PEG perspectives with existing management and analyst signals in one screen.

Reversal conditions: Reconsider if valuation strategy shifts away from ratio-based screening, if business rules require non-positive ratio interpretation, or if a unified scoring model replaces explicit top/bottom indicator cards.


Decision: Enforce Public Demo Parity with Secure Dashboard Under Confidentiality Constraints

Status: current
Date: 2026-07-09
Source: jul-26_dashboard-demo-parity.md

Context

The public demo had diverged from the secure dashboard, showing a reduced set of cards and weaker decision context. This diluted product credibility and created inconsistency between what users could evaluate publicly and what authenticated users actually used. In parallel, the secure dashboard still rendered legacy summary blocks that no longer added business value.

Details

Resolution: enforce near-parity between /demo and /dashboard for retained business cards, while removing obsolete legacy blocks from the secure view. Use a shared card payload contract for both routes and keep demo data deterministic and confidentiality-safe.

Operational rules: - keep retained business cards aligned across secure and demo views; - remove legacy blocks (recommended decisions, weighted P/E paragraph, legacy summary counters); - bound demo exposure to a small deterministic sample (up to 10 titles, single coherent snapshot); - preserve card-title consistency and visual language between secure and demo.

Impact: the demo now reflects real product depth, secure view readability improves, and AI/context consumers can rely on one coherent card model across public and authenticated surfaces.

Reversal conditions: reconsider only if public demo goals change from product-fidelity to simplified marketing preview, or if compliance requires an even stricter demo abstraction than the current capped deterministic model.


Decision: Enforce Demo Indicator Coherence Rules as Business Contracts

Status: current
Date: 2026-08-21
Source: aug-26_demo-indicators-consistency-fix.md

Context

The public demo route is a credibility surface for external evaluation. The UI rendered correctly, but key business rules were not explicitly guaranteed: performer sign consistency, mutual exclusivity across 52-week cards, exclusivity across analyst high/low target cards, and realistic target proximity to displayed prices.

Details

Resolution: enforce explicit coherence rules in the demo context builder and lock them with a dedicated regression test contract (tc-demo14) in tests/test_demo.py.

Operational rules: - top performers list includes only strictly positive variations; - bottom performers list includes only strictly negative variations; - one ticker can belong to only one 52-week side (high or low), based on shortest-distance allocation; - one ticker can belong to only one analyst-target side (high or low); - demo analyst targets stay within a realistic 0-10% band around displayed prices.

Impact: the public demo remains business-coherent and trustworthy without increasing sample size. Rule integrity is now regression-protected by automated tests, reducing the risk of future visual-only or data-only drifts.

Reversal conditions: reconsider only if demo goals shift from realistic product fidelity to synthetic marketing abstraction, or if portfolio-rule semantics evolve beyond sign/exclusivity contracts.


Decision: Display Native Currency and Convert Only Divergent Current Prices

Status: current
Date: 2026-08-26
Source: aug-26_native-currency-display-and-split-totals.md

Context

The portfolio contains Canadian and U.S. holdings, while the application needs to preserve source-data fidelity for 52-week thresholds and provide meaningful currency-separated totals. A single conversion rule for every value would make threshold values misleading and obscure the native currency of each holding.

Details

Resolution: derive portfolio currency from the ticker (TSE: => CAD, otherwise USD) and display holding prices in that currency. Apply USD-to-CAD conversion only to a current price when the provider currency differs from the portfolio currency. Keep an_haut and an_bas unchanged from the source CSV; legacy an_haut_cad and an_bas_cad remain schema-compatible raw copies.

Operational rules: - dashboard totals are split into CAD and USD; the consolidated CAD total uses the persisted cad_value values from the latest snapshot, matching the portfolio chart; - cad_value is used only for consolidated totals, chart history, and consolidated performance. It is prohibited for native holding display and for 52-week comparisons; - holding pages and 52-week cards use valeur with devise, while an_haut and an_bas remain raw CSV thresholds; - the dashboard displays the rate applied to the latest snapshot and its snapshot date. It must not depend on a web-container-local FX state file; - exchange-rate retrieval reuses recent local state before calling APILayer; - every successful API rate updates logs/fx_rate_state.json and config.ini; - API failure falls back to the last known rate, then the configured rate; import remains fail-open.

Impact: native display, raw threshold fidelity, and currency-aware totals are consistent across the secure dashboard and demo views while API quota consumption is controlled.

Reversal conditions: reconsider if the portfolio currency model expands beyond the current ticker-prefix rule, if the source provider changes its currency contract, or if the product requires a different consolidation currency.


Decision: Adaptive Chart Density by Recency (Quarterly/Monthly/Weekly/Daily)

Status: current Date: 2026-08-29 Source: specs/021-graph-historical-density/

Context

The demo stock detail page (/demo/titre/<ticker>) only had 2 Snapshot History entries per stock, showing no credible trend. Separately, the secure stock detail chart (/titre/<id>) plotted one point per raw daily entry, which becomes unreadable once a stock accumulates more than a year of near-daily history.

Details

Resolution: - Demo: _build_demo_historique_pour_titre (in code_source_simule/flask_app.py) now generates 8 additional weekly entries via deterministic linear interpolation toward the existing June 28, 2026 price, for a total of 10 entries per stock — staying within the existing 10-entry demo privacy cap (no cap increase). - Secure app: a new pure function bucket_history_for_chart(historique_trie, today=None) in code_source_simule/portfolio_metrics.py groups raw entries into one point per quarter (years before the current one), per month (current year, completed quarters), per week (current quarter, completed weeks), or per day (current week). When a period has multiple entries, only the most recent one is kept. Empty periods never produce an artificial point. - The "Snapshot History" table always renders 100% of raw entries regardless of chart density; only the chart's labels/valeurs are filtered.

Impact: the demo shows a credible price trend without exceeding its privacy sample cap; the secure chart stays readable over long histories while the underlying data table remains fully auditable.

Reversal conditions: reconsider if the demo privacy cap changes, if the quarter/week boundary rules need to follow a non-calendar convention (e.g., fiscal year), or if a different representative-point rule (e.g., average instead of most-recent) is required.


Decision: Keep Investor Transactions Separate from Market Snapshots

Status: current
Date: 2026-08-30
Source: specs/022-holding-transactions/

Context

Holding pages previously showed only market snapshots. Those entries describe observed prices and must remain stable for charts and portfolio indicators, while investor purchases and sales need their own editable history.

Details

Resolution: Add an additive transactions ledger linked to holdings, separate from historique. A dedicated pre-start step loads the supplied CSV idempotently during deployment, before Gunicorn starts and independently from the daily holding pipeline. It retries database startup but always releases the existing web server if loading remains unavailable. An explicit dry-run command is retained and no web upload is exposed. Historical shares and unit prices preserve up to four decimals, legacy zero-share rows and one-cent rounding differences; new UI entries remain positive, calculated with ROUND_HALF_UP and limited to two decimals. When the source starts after a position was opened, the minimum required opening balance is inferred so the full supplied history remains visible while later overselling stays blocked.

The public demo mirrors visible transaction management using a deterministic session-only copy. Every demo holding starts with transactions. Deleting the last transaction requires explicit confirmation and temporarily removes the holding from the demo dashboard and makes its detail page unavailable. The result of an operation is shown once after redirect; the next page refresh restores the complete deterministic baseline. Nothing is written to the secure database, and the 30-minute inactivity limit remains a fallback reset.

Balance replay counts BUY before SELL when operations share one date. Secure create, edit and delete routes lock the holding row and its transaction rows with SELECT ... FOR UPDATE before validation; using the holding as the common lock also serializes creation into an empty ledger. Business exceptions expose stable codes so English interface messages do not depend on matching French validation text.

Portfolio synchronization deletes a removed holding's transaction rows before its snapshots and holding row, preserving foreign-key integrity. Database migrations have one owner: the application pre-start process applies them before Gunicorn; the deployment workflow no longer starts a competing migration process. Mutating browser tests run only against an isolated local Flask server and in-memory database, using installed Google Chrome explicitly.

Impact: Holding detail pages now distinguish market observations from investor decisions. The secure ledger remains auditable, while demo behavior stays isolated and safe for public use.

Reversal conditions: Reconsider if the product introduces multiple administrator portfolios, requires a different monetary precision, or needs regulated transaction audit rules beyond this ledger.


Decision: Use a Manual Chronicle Review for Copilot Session Improvement

Status: current
Date: 2026-09-02
Source: sep-26_copilot-session-review-agent.md

Context

Recent Copilot work included long sessions, repeated continuation prompts, and multiple documentation and validation follow-ups. A permanent observer would add context and token overhead, conflicting with the primary goal of reducing consumption.

Details

Resolution: use the repository agent speckit.session-review.agent.md manually once at session closure. The agent queries Chronicle in two passes, reads no more than five relevant activity reports, and adds bounded process-improvement recommendations to the matching bilingual report.

Impact: Spec Kit remains responsible for delivery planning and implementation, while Chronicle supplies a lightweight retrospective layer. Local-only measurements use turn count, duration, prompt size, compaction, and repeated context reads as proxies because authoritative per-token data is unavailable locally.

Reversal conditions: reconsider if VS Code provides a reliable low-cost session-close trigger, if local Chronicle gains authoritative token accounting, or if several sessions show that the retrospective costs more than the context it saves.


Incidents & Lessons

V1 placeholder. Incidents discovered during V1 implementation will be documented here. These entries will be migrated to ../06_lessons-learned/ when V2 is delivered.