From f88c4cdcdbffbf65928671b3acbfa51f6b1cdf48 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Thu, 13 Aug 2026 10:20:01 -0400 Subject: [PATCH 01/13] Reconcile post-merge product truth --- ROADMAP.md | 4 +- ..._RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md | 51 ++++++++++--------- src/project_status.py | 17 +++++-- src/research_workspace.py | 26 ++++++---- tests/test_project_status.py | 17 +++++-- tests/test_public_v1_release_docs.py | 50 +++++++++++++----- tests/test_research_workspace.py | 34 +++++++++++++ 7 files changed, 139 insertions(+), 60 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 24819f07..3a78c835 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -64,7 +64,7 @@ Stage A-G labels are continuation maturity lanes only; they do not replace the n Company Workbench HTML Research Brief — Historical pre-fix evidence: Task 4 local matrix completed at `c8c313b9c`. Modal modifiers and active exposure fail closed. Broad-review repairs must be evaluated only through direct current-head local and exact-head CI evidence; their presence alone establishes neither gate. Exact-head repair evidence: commit `b69badfc80424d3a97fae5f77706aa6ed1533167` passed the 5,828-test full suite, the required dashboard, render, HTML, accessibility, public, and hygiene gates, branch/PR synchronization, and exact-head GitHub Actions run `30726301045`. The brief downloads an immutable offline view of existing saved evidence and prepared Python scenario math, preserves independent field gates and research-only wording, writes no repository artifact, and does not activate readiness or create a new calculation engine. Pilot packaging remains blocked on readiness freshness and source proof. Source rights, current data, hosted operation, human and screen-reader accessibility, independent workflow sessions, screening validation, and probability calibration remain open gates. Local engineering evidence does not establish source rights, current-market data, readiness activation, a new or professional line-item model, hosted operation, human or screen-reader conformance, independent validation, market fit, screening alpha, or probability calibration. ## Next: Ordered Maturity Work -Complete the direct local matrix and current-head local evidence first, then select the first incomplete safe roadmap priority. Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization. If the next gate needs an unavailable source, account, environment, reviewer, or elapsed event history, classify it once under **Externally blocked** and continue to the next executable priority. Passing local tests never completes an external gate. Evidence publication, snapshot, and retrieval timestamps must all be at or before the cutoff. +PR #113 merged the verified engineering closure into `main`; exact-head CI run `31704776477` passed. Do not rerun or resynchronize that completed slice without changed product bytes or separate owner authorization. Select the first incomplete safe roadmap priority. If the next gate needs an unavailable source, account, environment, reviewer, or elapsed event history, classify it once under **Externally blocked** and continue to the next executable priority. Passing local tests never completes an external gate. Evidence publication, snapshot, and retrieval timestamps must all be at or before the cutoff. Documentation and routing now name Personal Research as the root default, Public and Operator as explicit modes, and legacy utilities as Operator-only compatibility surfaces. Reopen this contract when current repository evidence reproduces drift. @@ -113,7 +113,7 @@ Final integrity commit `e3a090dba` ensures confirmation appends only the receipt Confirmation-integrity commit `5a6c55921` binds every displayed preview field, preview time, and destination label to the exact receipt. If an append raises after it may have written, confirmation returns one-shot `save_pending_reload` with the exact record ID unless the locked ledger is provably unchanged; it never invites a blind duplicate retry. -Priority 4's local validator is frozen; its permitted real-data exit gate remains externally incomplete. Priority 6's provider-neutral authorization contract is complete locally; hosted implementation remains environment-dependent. Complete the direct local matrix and current-head local evidence first, then select the first incomplete safe roadmap priority. Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization. +Priority 4's local validator is frozen; its permitted real-data exit gate remains externally incomplete. Priority 6's provider-neutral authorization contract is complete locally; hosted implementation remains environment-dependent. The merged local matrix and exact-head CI are completed engineering evidence; select the first incomplete safe roadmap priority and require separate owner authorization for any future remote synchronization or release action. ### Priority 4 — Point-in-time benchmark and universe foundation diff --git a/docs/internal/COMMERCIAL_RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md b/docs/internal/COMMERCIAL_RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md index 502db655..86fd8aa6 100644 --- a/docs/internal/COMMERCIAL_RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md +++ b/docs/internal/COMMERCIAL_RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md @@ -7,7 +7,9 @@ Use this prompt to continue the Stock Research Command Center in a new Codex tas Continue the Stock Research Command Center in: -/Users/yjian070/Documents/New project/.worktrees/personal-research-mode-mvp +/Users/yjian070/Documents/New project + +Start reads from current `main`. If a local repair is required, create a fresh worktree from current `main`; do not edit `main` directly and do not reuse the merged feature worktree. Objective: @@ -22,13 +24,13 @@ Do not run readiness rebuilds or generated-artifact commands without explicit ap Persistence contract: 1. Start every continuation from live repository, roadmap, generated-artifact, test, runtime, remote-branch, and PR truth. Chat memory and the expected-state notes below are navigation aids only. -2. Continue automatically while any safe, meaningful, in-scope local task remains. Ordinary reversible local implementation, testing, documentation, and local commits are authorized; draft-PR updates are not. +2. Continue automatically while any safe, meaningful, in-scope local task remains. Ordinary reversible local implementation, testing, documentation, and local commits are authorized; new pushes, pull requests, or external publication are not. 3. Do not mark the goal blocked because one lane, provider, dataset, hosted account, reviewer, source, or calibration cohort is unavailable when another executable workstream exists. 4. When an external dependency is unavailable, classify it once, record the exact unblock condition and last evidence, avoid identical retry loops, and move immediately to the next executable local task. 5. Recheck an external dependency only when a credential, supplied dataset, account, URL, reviewer cohort, provider entitlement, source-rights decision, or other relevant external state has verifiably changed. 6. Do not repeat exhausted provider probes, broad-coverage refreshes, speculative peer sourcing, or identical source-limit loops. -7. Work one coherent independently tested slice at a time. Finish verification, exact staging, local commit, and roadmap/docs updates before beginning the next slice. Do not push or update the draft PR without separate owner authorization. -8. Preserve explicit approval requirements for merging, public deployment, external account changes, credential use, destructive actions, purchases, public communication, or material scope expansion. +7. Work one coherent independently tested slice at a time. Finish verification, exact staging, local commit, and roadmap/docs updates before beginning the next slice. Do not push or create another pull request without separate owner authorization. +8. Preserve explicit approval requirements for future merges, public deployment, external account changes, credential use, destructive actions, purchases, public communication, or material scope expansion. 9. Never fabricate or infer data, forecasts, probabilities, evidence, events, peers, roles, comparability, outcomes, timestamps, sources, rights, reviewer results, hosted properties, recommendations, or completion evidence. 10. Do not mark the objective complete until the requirement-by-requirement completion audit directly proves every applicable exit gate. Passing local tests is not proof of source access, hosting, external beta validation, evidence depth, calibration, or operating maturity. 11. If every remaining task genuinely requires unavailable external input or new authority, leave completion unclaimed, produce the exact dependency ledger and resume checklist, and follow the active goal system's strict blocked audit. “Non-blocking” means pivoting to executable work, not retrying an unavailable dependency forever or pretending it is complete. @@ -36,11 +38,12 @@ Persistence contract: Expected lineage to verify, never assume: -- Branch: `codex/personal-research-mode-mvp`. -- Draft PR: https://github.com/YuzeJ21/Stock-Analysis/pull/113. -- Performance sampling reconciliation implementation anchor: `6328c8cead7c27cb901e7878cd6d7d23fa11bb0e`. Warm shell/first-useful p90 and cold shell/first-useful maximum separately enforce the unchanged one-second and three-second limits. A controlled local Chrome run recorded 48 successful samples with zero failures; the aggregate release check, 4,474-test full suite, six-route/two-viewport accessibility browser gate, state harness, push, and exact-head GitHub Actions run `30634355602` passed. PR #113 remained open, draft, and mergeable. This is local engineering evidence only; later descendants must reverify it, and category-specific failures must not be hidden by unchanged retry loops. -- Answer-first workflow design anchor: `0dd9a56d3` or a later verified descendant. Discover truth-separation implementation anchors: `ea92d2c6e`, `df6e72b11`, `c084cc274`, and accessibility contract `38e0cef0f`, or later verified descendants. These commits separate strict screen eligibility from readiness-only alphabetical saved-company browsing without reading legacy ranking outputs. They remain local implementation evidence until current-head full, browser, release, push, draft-PR, and exact-head CI gates pass. -- Roadmap truth-reconciliation contract: `ROADMAP.md` is the concise current decision index with `Now`, `Next`, `Externally blocked`, `Later`, and `Completed with evidence`; detailed remediation history stays in its named evidence documents. Reverify the line-budget contract, focused documentation tests, release gates, commit, push, draft-PR update, and exact-head CI before calling the reconciliation slice complete. +- Default branch: `main` at merge commit `0e2c984ac6fd357f891feb0a6864230892a76095` when last verified. +- Merged source branch: `codex/personal-research-mode-mvp` at tested head `f5cb33c2de9e8341d71ceaa69e279e30d269ca14`. +- PR #113 merged into `main`: https://github.com/YuzeJ21/Stock-Analysis/pull/113. Exact-head GitHub Actions run `31704776477` passed, and the final pre-merge local suite passed 6,719 tests. Reverify all of these facts rather than treating this paragraph as live state. +- Performance sampling reconciliation implementation anchor: `6328c8cead7c27cb901e7878cd6d7d23fa11bb0e`. Warm shell/first-useful p90 and cold shell/first-useful maximum separately enforce the unchanged one-second and three-second limits. A controlled local Chrome run recorded 48 successful samples with zero failures; the aggregate release check, 4,474-test full suite, six-route/two-viewport accessibility browser gate, state harness, push, and exact-head GitHub Actions run `30634355602` passed. At that historical anchor PR #113 remained open, draft, and mergeable; the merged closure recorded below supersedes only that branch-state observation, not the evidence limits. +- Answer-first workflow design anchor: `0dd9a56d3` or a later verified descendant. Discover truth-separation implementation anchors: `ea92d2c6e`, `df6e72b11`, `c084cc274`, and accessibility contract `38e0cef0f`, or later verified descendants. These commits separate strict screen eligibility from readiness-only alphabetical saved-company browsing without reading legacy ranking outputs. They were local implementation evidence at those historical anchors; the current merged closure below is the active release-routing boundary. +- Roadmap truth-reconciliation contract: `ROADMAP.md` is the concise current decision index with `Now`, `Next`, `Externally blocked`, `Later`, and `Completed with evidence`; detailed remediation history stays in its named evidence documents. Reverify the line-budget contract, focused documentation tests, and only the release gates invalidated by current changed bytes before calling a later reconciliation slice complete. - Priority 4 freeze synchronization anchor: remote commit `69c49968e77bfd55fa259695089e1f34ac2fddfb`; exact-head GitHub Actions run `30185232040` passed. Reverify both instead of treating the local read-only index as authoritative remote state. - Evidence-quality lineage anchor: commit `781ba2481` or a later verified descendant. - SEC quarterly cash-generation pilot anchor: commit `a262eda9f` or a later verified descendant. @@ -77,7 +80,7 @@ Expected lineage to verify, never assume: - Research Decision Lab approved-design anchor: commit `54e06e1c3`; composition anchor: `1cfed7490`; Company Workbench anchor: `a4786bb25`; Monitor anchor: `c7ad977b3`, or later verified descendants. Read `docs/superpowers/specs/2026-07-22-research-decision-lab-design.md`, reverify the current implementation and exact-head CI, and do not treat the design commit alone as product evidence. - Point-in-time universe production-validator lineage anchor: commit `1361472bce6d23cc537ef222c3735bb640c9838a` or a later verified descendant. Task 8 documentation-evidence reliance requires commit `1ece7a3e4adc70450d6318c06571cc8bf54368b0` or a later descendant. Reverify the local implementation and every applicable current gate; neither anchor is permitted-real-data, provider-rights, independent-review, hosted, or commercial-validation evidence. - Do not assume branch cleanliness, push state, or upstream alignment. The tracked PR readiness snapshot remains the June 7 snapshot and is stale under the declared-date policy; an excluded local generated working-data snapshot may be date-current but is not committed PR evidence. Reverify its declared date through the current read-only commands and use `make readiness-preview TOP_N=20`; the verified local run reported zero stable readiness changes, which does not authorize staging or a readiness rebuild. -- PR #113 must remain open and draft. Do not merge it. +- PR #113 is historical merged evidence, not an active work queue. Do not reopen, rewrite, or treat it as authorization for a new push, pull request, deployment, publication, or release. - Generated CSV, JSON, readiness reports, stock reports, sample reports, screenshots, browser timing output, and other generated churn must remain excluded unless one exact artifact is intentionally reviewed and explicitly required. Current locally implemented capabilities to verify: @@ -95,7 +98,7 @@ Current locally implemented capabilities to verify: - Prospective-only per-field proof operations: `make prospective-field-proof-status`, `make prospective-field-proof-preview INPUT= AS_OF=`, and explicit `make prospective-field-proof-record INPUT= AS_OF= PREVIEW_RECEIPT= CONFIRM_REVIEWED=1`. An absent ledger is a valid empty state; legacy narrative proof is not upgraded, and no sample rows are product evidence. Preview reports `technical_write_eligible` and `commercial_evidence_eligible` independently, and its preview receipt binds ledger, input, cutoff, commercial mode, and source-rights registry. The ledger does not activate readiness, does not update canonical data, does not update proof-readiness reconciliation, and does not activate Company Workbench. Any mapping requires a separate design. - Historical Valuation Regime, Research Outcome Review, and Catalyst Evidence Timeline. Historical-valuation numeric loading rejects blank or malformed numerator/denominator evidence per row rather than coercing it to zero or discarding valid sibling rows. - Forward View, Scenario Lab, Source Freshness Timeline, Research Comparison, Peer Read-Through Map, and Decision-Process Scorecard. -- Local Decision Lab implementation is complete when current repository evidence reconfirms the immutable six-lane contract, Workbench placement, Monitor stable-order Research Discipline Review, per-ticker fail-closed isolation, responsive runtime behavior, release gates, exact staging, push, draft-PR update, and exact-head CI. It adds no route, ledger, readiness state, transaction field, position sizing, stop/profit rule, recommendation, or broker action. Do not reimplement it unless a current regression is directly reproduced. +- Local Decision Lab implementation is complete as a merged milestone when current repository evidence reconfirms the immutable six-lane contract, Workbench placement, Monitor stable-order Research Discipline Review, per-ticker fail-closed isolation, responsive runtime behavior, and the recorded merged evidence lineage. It adds no route, ledger, readiness state, transaction field, position sizing, stop/profit rule, recommendation, or broker action. Do not reimplement it unless a current regression is directly reproduced. - The local Decision Lab does not prove source coverage, predictive accuracy, investment performance, independent adoption, hosted reliability, commercial demand, competitive superiority, or product-market fit. - Fail-closed provenance, source-rights, freshness, candidate-context, and synthetic-fixture controls. - Local market-observation recency across Research Desk, Discover, Company Workbench, and Monitor: one read-only local `prices.csv` evaluation per dashboard run, using the dashboard UTC review date. The evaluator independently fails closed for unreadable, invalid, future, or missing observations and applies an exact seven-calendar-day policy. It is not an exchange-session SLA, does not make saved readiness current, and does not prove permitted market-data source rights or hosted freshness; those remain external gates. @@ -190,7 +193,7 @@ Current external dependency classifications to verify once, then avoid looping: - Point-in-time consensus and rights: `permitted_point_in_time_consensus_and_rights_required`. Last observed: no permitted point-in-time consensus input or approved exact-source consensus rights evidence is on record. Exact unblock condition: configure a permitted point-in-time provider or supply a reviewed CSV, approve rights for that exact source and every populated consensus field, then run source review and collection preview without inferring a provider or recording readiness. - Hosted account and controls: `hosted_account_and_controls_required`. Last observed: no verified hosted URL or enforced hosted access boundary is on record. Exact unblock condition: provide an intentional host account, a directly verified URL, enforced claimed access controls, and user/workspace isolation in that environment. -- Independent reviewers: `independent_reviewers_required`. Last observed: no independent human GitHub pull-request review or completed independent beta-review cohort is on record. Exact unblock condition: obtain independent human GitHub review of PR #113 for the engineering-review gate and complete controlled sessions with 10-20 independent task-based reviewers, recording reproducible observations for beta validation. Subagent and automated review satisfy neither condition. +- Independent reviewers: `independent_reviewers_required`. Last observed: the merged engineering pull request has no independent human review, and no completed independent beta-review cohort is on record. Exact unblock condition: obtain a genuine independent review of the current default-branch engineering state or a later reviewed pull request, and separately complete controlled sessions with 10-20 independent task-based reviewers while recording reproducible observations for beta validation. Subagent and automated review satisfy neither condition. - Trusted peer/source review: `trustworthy_peer_source_and_review_required`. Last observed: no trustworthy reviewed peer relationship evidence is on record for the bounded pilot. Exact unblock condition: supply a trustworthy relationship source and review capacity sufficient to preserve one bounded reviewed peer relationship, including role, rationale, comparability, source/as-of evidence, and explicit valuation-anchor eligibility. - Calibration cohort: `calibration_cohort_required`. Last observed: calibration evidence remains insufficient and numerical probability is withheld. If one immutable operator bundle is supplied, run `make calibration-evidence-bundle-preview BUNDLE=` once; `invalid`, `blocked`, and `contract_consistent_review_required` are review states only. The preview writes no artifact, does not activate readiness, and probability remains withheld. Synthetic fixtures remain test-only. Classify an absent or unchanged bundle once and move to another executable lane. Exact unblock condition: accumulate at least 100 leakage-safe out-of-sample events from permitted point-in-time inputs, obtain source-rights and independent review, and pass the declared Brier-score, calibration-bin, benchmark-improvement, exact-identity, chronology, and backtest gates. - Operated owner/incident/rollback capacity: `operated_owner_incident_rollback_capacity_required`. Last observed: no named operated owner or directly rehearsed incident and rollback capacity is on record. Exact unblock condition: provide a named owner and directly rehearsed incident and rollback capacity, then directly verify audit, retention, entitlements, monitoring, and health-check operation. A local runbook or hosted URL is insufficient. @@ -200,7 +203,7 @@ Current external dependency classifications to verify once, then avoid looping: Approved Next-Stage Maturity Program: -This is the authoritative priority order after the completed Research Decision Lab. The Calm Institutional Workspace local engineering closure is complete at `49123a989dae263e8c125ad3032bf96d0107853d`, including the ordered 90-cell route/viewport/zoom acceptance and current-byte local test evidence. Select the first incomplete safe roadmap priority from current repository truth rather than rerunning that completed program. A push, draft-PR update, merge, deploy, remote synchronization, or exact-head CI run requires separate owner authorization and is not part of this local task. When the next requirement is unavailable, classify its last evidence and exact unblock condition once, leave it incomplete, and move to the next safe executable priority. Re-enter a blocked priority only after relevant external state changes. +This is the authoritative priority order after the completed Research Decision Lab. The Calm Institutional Workspace local engineering closure is complete at `49123a989dae263e8c125ad3032bf96d0107853d`, including the ordered 90-cell route/viewport/zoom acceptance and current-byte local test evidence. PR #113 later merged the verified descendant into `main`; that completed synchronization does not authorize another push, pull request, merge, deploy, publication, release, or exact-head CI run. Select the first incomplete safe roadmap priority from current repository truth rather than rerunning that completed program. When the next requirement is unavailable, classify its last evidence and exact unblock condition once, leave it incomplete, and move to the next safe executable priority. Re-enter a blocked priority only after relevant external state changes. A blocked priority does not become complete, and moving past it does not weaken its exit gate. Local contracts, fixtures, screenshots, subagent reviews, and green tests cannot substitute for direct source, hosted, accessibility, independent-user, or calibration evidence. Continue cycling through the ordered program while safe executable work exists; overall completion requires direct current evidence for every applicable priority. @@ -212,7 +215,7 @@ Modal modifiers and active exposure fail closed. The only active documented-resu Current local execution queue: -Base workspace product slice completed at `49123a989dae263e8c125ad3032bf96d0107853d`: Discover truth separation, the Company Workbench primary brief, Monitor consolidation, Research Desk simplification, and shared-shell cleanup were implemented and locally verified. The current descendant worktree refines that routing without attributing the refinement to the older anchor. Strict eligibility remains empty when required evidence is incomplete and never relaxes thresholds. Research Desk now renders one **Today's Research Brief** instead of a weekly-card row plus four competing answers; it routes to Monitor for saved follow-up items, Data Health for a separate saved-source freshness condition, and Discover when neither is due, while keeping all supporting evidence under Advanced. Alphabetical saved-company browsing remains available from readiness-only identities without importing ranking-adjacent fields or legacy outputs. Workbench shows one Company Brief with usable evidence, withheld evidence, change state, one authoritative next task, the stop rule, and a ticker-bound Data Health action before any detailed module; one explicit session-local action restores the unchanged detailed evidence modules without writing data or changing readiness. Monitor renders one five-panel **Follow-up Queue** instead of Evidence Monitor Brief, Research Discipline Review, and Research change monitor as three competing primary answers. Its empty state appears once, preserves the external-event boundary, and returns to Discover; full process identities and source-change evidence remain under `Advanced: Monitor evidence`. Personal Research now has one in-content Personal research workflow navigation authority; no workspace selector or repeated Research page choices remain in the sidebar, and the Research main column no longer renders the Operator command/readiness header or broad profile strip before the route answer. Public and Operator remain explicit modes, direct links and ticker parameters remain supported, and legacy utilities stay quarantined in Operator compatibility surfaces. Current synchronized exact-head closure is `d96d7af8f10c8e6c63096355d2abbf109de4f1f2`: PR #113 is open and draft at that exact head, exact-head CI is green in run `31612484529`, and the affected and ordered browser packets are local automated engineering evidence. Independent human review remains absent, the feature is not on the default branch, and marking ready, merging, publishing, or presenting it as a stable release still requires explicit owner action. No push, draft-PR update, merge, or deploy is part of this modernization task without separate owner authorization. Pilot readiness remains blocked on `working_artifact_uncommitted` readiness evidence and incomplete source proof. The first external data unblock remains Priority 4's bounded permitted point-in-time benchmark/universe package; Priority 5's permitted consensus and genuinely reviewed peer evidence follows separately. +Base workspace product slice completed at `49123a989dae263e8c125ad3032bf96d0107853d`: Discover truth separation, the Company Workbench primary brief, Monitor consolidation, Research Desk simplification, and shared-shell cleanup were implemented and locally verified. Later descendants refined that routing without attributing the refinement to the older anchor. Strict eligibility remains empty when required evidence is incomplete and never relaxes thresholds. Research Desk now renders one **Today's Research Brief** instead of a weekly-card row plus four competing answers; it routes to Monitor for saved follow-up items, Data Health for a separate saved-source freshness condition, and Discover when neither is due, while keeping all supporting evidence under Advanced. Alphabetical saved-company browsing remains available from readiness-only identities without importing ranking-adjacent fields or legacy outputs. Workbench shows one Company Brief with usable evidence, withheld evidence, change state, one authoritative next task, the stop rule, and a ticker-bound Data Health action before any detailed module; one explicit session-local action restores the unchanged detailed evidence modules without writing data or changing readiness. Monitor renders one five-panel **Follow-up Queue** instead of Evidence Monitor Brief, Research Discipline Review, and Research change monitor as three competing primary answers. Its empty state appears once, preserves the external-event boundary, and returns to Discover; full process identities and source-change evidence remain under `Advanced: Monitor evidence`. Personal Research now has one in-content Personal research workflow navigation authority; no workspace selector or repeated Research page choices remain in the sidebar, and the Research main column no longer renders the Operator command/readiness header or broad profile strip before the route answer. Public and Operator remain explicit modes, direct links and ticker parameters remain supported, and legacy utilities stay quarantined in Operator compatibility surfaces. Current merged closure is tested feature head `f5cb33c2de9e8341d71ceaa69e279e30d269ca14` and merge commit `0e2c984ac6fd357f891feb0a6864230892a76095`: PR #113 is merged, exact-head CI is green in run `31704776477`, and the final pre-merge local suite passed 6,719 tests. Independent human review remains absent; default-branch presence and automated checks do not prove hosted operation, source rights, human accessibility, researcher validation, calibration, or owner approval for external publication. No new push, pull request, deploy, publication, or release is part of this continuation without separate owner authorization. Pilot readiness remains blocked on current readiness/source proof rather than being unlocked by the merge. The first external data unblock remains Priority 4's bounded permitted point-in-time benchmark/universe package; Priority 5's permitted consensus and genuinely reviewed peer evidence follows separately. Completed local item: `1. Add shared quant provenance/recency eligibility without coupling readiness.` This former queue item is implemented at `195ea18da9d1d6e06c36f8320509ccde46cdaa57` and is not an active task. @@ -287,7 +290,7 @@ A saved record cannot change readiness, forecasts, probabilities, recommendation Priority 4's local validator is frozen; its permitted real-data exit gate remains externally incomplete. Priority 6's provider-neutral authorization contract is complete locally; hosted implementation remains environment-dependent. -If the current branch head lacks the direct local matrix or current-head local evidence, complete those local gates first; otherwise select the first incomplete safe roadmap priority. Remote synchronization, a draft-PR update, and exact-head CI remain separate owner-authorized work. +If current changed product bytes lack directly bound local matrix or current-head evidence, complete only the invalidated gates first; otherwise select the first incomplete safe roadmap priority. The PR #113 synchronization and exact-head CI are completed historical evidence. Any new remote synchronization, pull request, merge, deployment, publication, or CI-triggering push remains separate owner-authorized work. That summary is necessary but not sufficient; the exact Priority 4 exit condition below also requires independent review, expected count/digest reproduction, and the partition gate. Priority 4 — Point-in-time benchmark and universe foundation @@ -404,7 +407,7 @@ Priority 10 — Separately approved hypothetical paper-position laboratory Execution order for each continuation: -1. Verify current branch, worktree, latest commits, upstream alignment, PR #113 draft status, ROADMAP.md, generated-artifact hygiene, current tests, and current product gates. +1. Verify current branch, worktree, latest commits, upstream alignment, PR #113 merged state and exact merge/head checks, ROADMAP.md, generated-artifact hygiene, current tests, and current product gates. 2. Review unresolved PR feedback and roadmap claims against live code and runtime behavior. 3. Before reusing a supporting proof outcome, run `make proof-readiness-reconciliation TOP_N=20`; keep `historical_supported_currently_blocked` lanes blocked, keep scope-only outcomes non-supporting at ticker level, route each current blocker to its named safe review, and move to fresh evidence or another executable lane. Never infer historical source, rights, scope, or cause from narrative proof; the implemented structured per-ticker/per-field record is prospective-only and does not retroactively upgrade narrative history. 4. Audit the complete user workflow: Research Desk -> Discover -> Company Workbench -> Monitor. @@ -415,7 +418,7 @@ Execution order for each continuation: 8. Keep technical evidence under Advanced unless it is required to explain the primary research answer. 9. Classify unavailable external dependencies once and move to the next executable local roadmap item. 10. Update tests, methodology, provenance, runbooks, ROADMAP.md, and this prompt when the verified stage or continuation contract changes. -11. Stage exact intentional paths only and commit the verified slice locally. Do not push or update PR #113 without separate owner authorization; keep it draft. +11. Stage exact intentional paths only and commit the verified slice locally. Do not push, create another pull request, deploy, publish, or release without separate owner authorization. PR #113 is already merged and is not an active update target. 12. Continue to the next safe executable item rather than ending merely because one slice is complete. Continuation maturity lanes: @@ -427,7 +430,7 @@ Numbered release stage gates: Stage 0 — Independent engineering legitimacy - Keep the minimal PR-only workflow read-only, least privilege, and free of providers, readiness generation, schedules, secrets, deployment, and artifact uploads. - Require a completed GitHub Actions result for the current PR revision; do not infer hosted execution from local workflow tests. -- Require independent human GitHub review of PR #113 and classify it separately from automated checks. +- Record that PR #113 merged without an independent human review. Any future engineering-review requirement must be satisfied by a genuine independent review of the current default-branch state or a later reviewed pull request; automated checks and subagents do not substitute for it. - Hosted automation passed on the recorded implementation lineage. Exit remains revision-specific: require the current revision's direct CI result and an explicitly known review state. A green check does not satisfy human review or any later source, hosted-preview, beta, evidence-depth, calibration, or operating gate. Stage 1 — Answer-first workflow hardening @@ -488,8 +491,8 @@ Verification after every meaningful implementation slice: - relevant commercial performance or release gate when workflow/runtime behavior changes; - `make staged-hygiene-check` and `git diff --cached --check` after exact staging. -Release-matrix boundary: the controller owns the full local release matrix. Push, -PR update, and hosted exact-head CI verification require separate owner authorization. A documentation-slice worker may +Release-matrix boundary: the controller owns the full local release matrix. A new push, +pull request, merge, deployment, publication, or hosted exact-head CI verification requires separate owner authorization. A documentation-slice worker may run only its focused docs/render checks, stage its exact reviewed files, and report the local evidence; it must not claim the full release matrix or hosted CI has passed. @@ -500,9 +503,9 @@ Git and artifact rules: - Stage exact reviewed product, code, documentation, test, and template paths only. - Never stage broad generated CSV, JSON, readiness-report, stock-report, sample-report, screenshot, or timing churn. One exact curated screenshot may be staged only when it is the explicitly reviewed product asset required by the current slice. - Commit only coherent verified slices. -- Do not push without separate owner authorization; if later authorized, push only to `codex/personal-research-mode-mvp`. -- Keep PR #113 draft; update it only after separate owner authorization. -- Keep PR #113 open and draft; do not merge, deploy, or mark it ready. +- Do not push without separate owner authorization. Do not reuse the merged `codex/personal-research-mode-mvp` branch as an inferred destination for new work. +- PR #113 is merged historical evidence. Do not reopen or rewrite it, and do not treat that merge as approval for another pull request. +- Do not deploy, publish, create a release, or merge future work without separate owner authorization. - The same 18 protected generated paths remain excluded, byte-for-byte unchanged, and unstaged unless separately reviewed and explicitly approved: - `data/analyst_estimates_readiness.csv` - `data/dcf_readiness.csv` @@ -522,7 +525,7 @@ Git and artifact rules: - `data/universe_master.csv` - `outputs/feature_readiness_summary.csv` - `outputs/peer_unlock_worklist.csv` -- Do not merge into main or deploy publicly without explicit approval. +- Do not merge future work into `main` or deploy publicly without explicit approval. Completion audit before any completion claim: diff --git a/src/project_status.py b/src/project_status.py index 1f4809ec..7fc42577 100644 --- a/src/project_status.py +++ b/src/project_status.py @@ -512,10 +512,19 @@ def _linkedin_stage_from_git_status(git_status_line: str | None) -> dict[str, st ), } return { - "State": "ready_for_manual_share", - "Evidence": "Public share gates pass; GitHub is synced; use GitHub link and curated screenshot.", - "Next Action": "Post or update LinkedIn manually using docs/LINKEDIN_PROJECT_BRIEF.md.", - "Completion Gate": "LinkedIn profile/card is updated by the account owner.", + "State": "manual_share_verification_required", + "Evidence": ( + "The reviewed feature is present on the aligned default branch, but Git alignment alone " + "does not prove current public-share checks or owner approval." + ), + "Next Action": ( + "Run make public-check, review docs/LINKEDIN_PROJECT_BRIEF.md, and proceed only after " + "separate owner authorization for the external profile update." + ), + "Completion Gate": ( + "Current-head public-check passes and the account owner separately approves and completes " + "the LinkedIn profile/card update." + ), } diff --git a/src/research_workspace.py b/src/research_workspace.py index 156f057b..1f3092dd 100644 --- a/src/research_workspace.py +++ b/src/research_workspace.py @@ -160,19 +160,22 @@ def build_research_desk_brief( else "A comparable saved before-and-after research snapshot is not available yet." ) if freshness_attention: - condition = ( - "saved readiness and market-observation freshness conditions" - if readiness_attention and observation_attention - else ( + if readiness_attention and observation_attention: + reason = ( + "No saved research item is due. Saved-readiness and market-observation " + "freshness both need Data Health review; neither is a saved research item " + "or a live-market alert." + ) + else: + condition = ( "a separate saved-readiness freshness condition" if readiness_attention else "a separate saved market-observation freshness condition" ) - ) - reason = ( - f"No saved research item is due. {condition.capitalize()} still needs " - "Data Health review; it is not a saved research item or a live-market alert." - ) + reason = ( + f"No saved research item is due. {condition.capitalize()} still needs " + "Data Health review; it is not a saved research item or a live-market alert." + ) next_action_label = "Open Data Health" next_action_url = "?mode=research&page=data-health" else: @@ -422,7 +425,7 @@ def monitor_primary_answer(queue: MonitorFollowUpQueue) -> str: return queue.empty_title if queue.freshness_attention_only: verb = ( - "need" + "both need" if queue.has_readiness_attention and queue.has_observation_attention else "needs" ) @@ -442,7 +445,8 @@ def monitor_freshness_condition_label( """Name the exact saved-readiness and/or market-observation condition.""" if queue.has_readiness_attention and queue.has_observation_attention: - label = "saved-readiness and market-observation freshness conditions" + label = "saved-readiness and market-observation freshness" + return label.capitalize() if sentence_case else label elif queue.has_readiness_attention: label = "saved-readiness freshness condition" else: diff --git a/tests/test_project_status.py b/tests/test_project_status.py index bba0309c..e34b5504 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -1209,7 +1209,10 @@ def test_project_status_stage_map_classifies_remaining_public_items( "FMP provider activation", "Peer readiness upgrade", ] - assert stage_rows[0]["State"] == "ready_for_manual_share" + assert stage_rows[0]["State"] == "manual_share_verification_required" + assert "make public-check" in stage_rows[0]["Next Action"] + assert "separate owner authorization" in stage_rows[0]["Next Action"] + assert "ready_for_manual_share" not in stage_rows[0].values() assert stage_rows[1]["State"] == "awaiting_external_setup" assert stage_rows[1]["Diagnostic State"] == "external_account_required" assert stage_rows[2]["State"] == "awaiting_external_setup" @@ -1285,13 +1288,17 @@ def test_project_status_labels_an_aligned_feature_branch_as_draft_engineering_pr assert "ready_for_manual_share" not in stage.values() -def test_project_status_keeps_an_aligned_default_branch_share_eligible(): - """Catches the fail-closed feature-branch rule blocking an actual stable branch.""" +def test_project_status_requires_public_gate_and_owner_review_on_aligned_default_branch(): + """Catches Git alignment alone being promoted into public-share readiness.""" stage = project_status._linkedin_stage_from_git_status("## main...origin/main") - assert stage["State"] == "ready_for_manual_share" - assert "use GitHub link" in stage["Evidence"] + assert stage["State"] == "manual_share_verification_required" + assert "default branch" in stage["Evidence"] + assert "does not prove" in stage["Evidence"] + assert "make public-check" in stage["Next Action"] + assert "separate owner authorization" in stage["Next Action"] + assert "ready_for_manual_share" not in stage.values() @pytest.mark.parametrize( diff --git a/tests/test_public_v1_release_docs.py b/tests/test_public_v1_release_docs.py index 56618ae9..e66f65b7 100644 --- a/tests/test_public_v1_release_docs.py +++ b/tests/test_public_v1_release_docs.py @@ -812,6 +812,12 @@ def test_commercial_beta_continuation_prompt_is_persistent_but_evidence_bound(): assert "Commercial Research Beta Continuation Contract" in roadmap assert "/goal" in prompt + active_path = prompt.split("Continue the Stock Research Command Center in:", 1)[1].split( + "Objective:", 1 + )[0] + assert "/Users/yjian070/Documents/New project" in active_path + assert ".worktrees/personal-research-mode-mvp" not in active_path + assert "fresh worktree from current `main`" in prompt assert "codex/personal-research-mode-mvp" in prompt assert "pull/113" in prompt assert "commit `781ba2481` or a later verified descendant" in prompt @@ -823,8 +829,14 @@ def test_commercial_beta_continuation_prompt_is_persistent_but_evidence_bound(): assert "Stage 1 — Answer-first workflow hardening" in prompt assert "Stage 6 — Operating maturity and product direction" in prompt assert "Never use `git add -A`" in prompt - assert "Keep PR #113 draft" in prompt - assert "Do not merge into main or deploy publicly without explicit approval" in prompt + assert "PR #113 merged into `main`" in prompt + assert "`0e2c984ac6fd357f891feb0a6864230892a76095`" in prompt + assert "`f5cb33c2de9e8341d71ceaa69e279e30d269ca14`" in prompt + assert "`31704776477`" in prompt + assert "6,719" in prompt + assert "PR #113 must remain open and draft" not in prompt + assert "Keep PR #113 draft" not in prompt + assert "the feature is not on the default branch" not in prompt assert "Generated CSV, JSON" in prompt assert "Point-in-time consensus and rights: `permitted_point_in_time_consensus_and_rights_required`" in prompt assert "Hosted account and controls: `hosted_account_and_controls_required`" in prompt @@ -1227,9 +1239,12 @@ def test_active_maturity_handoff_matches_current_sync_priority_and_ui_contracts( assert "working_artifact_uncommitted" in text assert "independent human review" in text - assert "d96d7af8f10c8e6c63096355d2abbf109de4f1f2" in continuation + assert "f5cb33c2de9e8341d71ceaa69e279e30d269ca14" in continuation + assert "0e2c984ac6fd357f891feb0a6864230892a76095" in continuation assert "PR #113" in continuation - assert "31612484529" in continuation + assert "31704776477" in continuation + assert "6,719" in continuation + assert "PR #113 is merged" in continuation assert ( "One permitted independently reviewed real point-in-time universe package, " @@ -1515,7 +1530,8 @@ def test_external_dependency_entries_own_distinct_conditions_and_last_observed_e assert hosted_only not in operated reviewers = bullets["Independent reviewers"] - assert "independent human GitHub review of PR #113" in reviewers + assert "genuine independent review of the current default-branch engineering state" in reviewers + assert "later reviewed pull request" in reviewers assert "when a human submits review evidence" not in reviewers assert "generic human review evidence" not in reviewers.lower() @@ -2309,13 +2325,18 @@ def test_broad_review_docs_use_durable_release_routing_and_fail_closed_boundarie assert durable_routing in document for document in (readme, roadmap, continuation): assert durable_routing not in document - for document in (readme, roadmap): + for document in (readme,): assert "Complete the direct local matrix and current-head local evidence first" in document assert "Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization" in document + assert "PR #113 merged the verified engineering closure into `main`" in roadmap + assert "exact-head CI run `31704776477` passed" in roadmap + assert "Do not rerun or resynchronize that completed slice" in roadmap + assert "Complete the direct local matrix and current-head local evidence first" not in roadmap assert "The Calm Institutional Workspace local engineering closure is complete" in continuation assert "49123a989dae263e8c125ad3032bf96d0107853d" in continuation assert "awaiting local quality closure" not in continuation - assert "A push, draft-PR update, merge, deploy, remote synchronization, or exact-head CI run requires separate owner authorization" in continuation + assert "that completed synchronization does not authorize another push" in continuation + assert "No new push, pull request, deploy, publication, or release" in continuation broad_review_boundary = ( "Broad-review repairs must be evaluated only through direct current-head local " "and exact-head CI evidence; their presence alone establishes neither gate." @@ -2476,9 +2497,10 @@ def test_workspace_modernization_docs_share_one_default_and_explicit_mode_contra ) assert release_first_claim not in readme assert release_first_claim not in roadmap - for document in (readme, roadmap): - assert "Complete the direct local matrix and current-head local evidence first" in document - assert "Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization" in document + assert "Complete the direct local matrix and current-head local evidence first" in readme + assert "Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization" in readme + assert "PR #113 merged the verified engineering closure into `main`" in roadmap + assert "Do not rerun or resynchronize that completed slice" in roadmap assert "Sidebar navigation remains the single public route chooser" not in dashboard_qa assert "in-content public workflow navigation is the route chooser" in dashboard_qa for stale_claim in ( @@ -2491,8 +2513,8 @@ def test_workspace_modernization_docs_share_one_default_and_explicit_mode_contra assert stale_claim not in continuation assert "one in-content Personal research workflow navigation" in continuation assert "Public and Operator remain explicit modes" in continuation - assert "No push, draft-PR update, merge, or deploy is part of this modernization task" in continuation - assert "Remote synchronization, a draft-PR update, and exact-head CI remain separate owner-authorized work" in continuation + assert "No new push, pull request, deploy, publication, or release is part of this continuation" in continuation + assert "The PR #113 synchronization and exact-head CI are completed historical evidence" in continuation for document in (readme, roadmap, personal, public, dashboard_qa, accessibility, operator, continuation): lowered = document.lower() assert "research-only" in lowered @@ -2612,7 +2634,7 @@ def test_html_research_brief_continuation_contract_preserves_anchor_and_exclusio assert "Company Workbench HTML Research Brief implementation anchors:" in continuation assert "`6ad7f34310652f1b172525a0b8f00becf874c44c`" in continuation assert "`8218af401`" in continuation - assert "Keep PR #113 open and draft" in continuation + assert "PR #113 is merged historical evidence" in continuation assert "The same 18 protected generated paths remain excluded" in continuation for path in ( "data/analyst_estimates_readiness.csv", @@ -2677,7 +2699,7 @@ def test_active_personal_research_docs_route_saved_items_freshness_and_zero_trut "docs/internal/COMMERCIAL_RESEARCH_BETA_CONTINUATION_GOAL_PROMPT.md" ) assert "Base workspace product slice completed at `49123a989" in continuation - assert "current descendant worktree refines that routing" in continuation + assert "Later descendants refined that routing" in continuation personal = _read("docs/PERSONAL_RESEARCH_MODE.md") assert "no saved research item is due but a saved-source freshness condition" in personal assert "Open Data Health" in personal diff --git a/tests/test_research_workspace.py b/tests/test_research_workspace.py index 00ba1d9d..92fc21aa 100644 --- a/tests/test_research_workspace.py +++ b/tests/test_research_workspace.py @@ -275,6 +275,40 @@ def test_desk_and_monitor_distinguish_zero_saved_items_from_stale_observation(): ) +def test_desk_and_monitor_name_combined_freshness_without_plural_mismatch(): + summary = _weekly_summary() + desk = build_research_desk_brief( + summary, + change_status="no_changes", + review_items=(), + freshness_state="stale", + freshness_message="Saved readiness is stale.", + observation_state="stale", + observation_message="Saved market observation is stale.", + ) + monitor = build_monitor_follow_up_queue( + summary, + (), + readiness_state="stale", + readiness_message="Saved readiness is stale.", + observation_state="stale", + observation_message="Saved market observation is stale.", + ) + + assert desk.reason == ( + "No saved research item is due. Saved-readiness and market-observation " + "freshness both need Data Health review; neither is a saved research item " + "or a live-market alert." + ) + assert monitor_primary_answer(monitor) == ( + "No saved research item is due. Saved-readiness and market-observation " + "freshness both need Data Health review." + ) + assert research_workspace.monitor_freshness_condition_label(monitor) == ( + "saved-readiness and market-observation freshness" + ) + + @pytest.mark.parametrize( ("unique_event_count", "item_count"), [(0, 1), (1, 2)], From b2f249c8050b532f89db4d37971e80c8435aef49 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Thu, 13 Aug 2026 11:34:15 -0400 Subject: [PATCH 02/13] Design peer lane readiness precedence --- ...lane-readiness-source-precedence-design.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md diff --git a/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md new file mode 100644 index 00000000..1faec3b4 --- /dev/null +++ b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md @@ -0,0 +1,73 @@ +# Peer Lane Readiness Source Precedence Design + +**Status:** Approved for bounded local implementation; no remote synchronization or generated-artifact mutation is authorized. + +## Problem + +The selected Data Health peer lane can display a stale count from +`outputs/project_status.json` even when the selected profile's saved readiness +summary contains a newer authoritative peer-readiness count. The current local +case renders `29 tickers have trusted peer context` from an older 3,541-row +project-status snapshot while the saved readiness report records 9 peer-ready +rows across 3,538 tickers. + +This conflicts with the product contract that current saved readiness is +authoritative for current lane availability. Project status remains useful for +operating context, source setup, and next-step routing, but it must not widen a +readiness-owned count over a present saved readiness value. + +## Decision + +`data_health_selected_lane_answer_cards()` will resolve readiness-owned counts +in this order: + +1. Use the selected saved readiness summary when any requested canonical or + alias key is present and parseable. A saved value of zero is authoritative; + it must not be treated as missing. +2. Fall back to the saved project-status summary only when the selected saved + readiness summary does not provide the requested count. +3. Report the count as unavailable when neither source provides it. + +The rule applies to the readiness-owned values displayed by the selected-lane +answer: price, fundamentals/input, DCF, peer, and blocked/locked-input counts. +Project-status-only source availability counts, lane inspection notes, and +recommended next-step context retain their current behavior. + +## Route Behavior + +Personal Research and Operator Data Health use the same source-precedence +contract. With the current local artifacts, both peer lanes must display the +saved peer-ready count of 9 rather than the stale project-status count of 29. +Candidate peers remain context only, and the answer must continue to avoid +recommendation, ranking, allocation, sizing, transaction, or performance +language. + +If saved readiness is missing, the route may use a complete available +project-status count. If both are missing or malformed, it must fail closed to +an unavailable count. No route may refresh, rebuild, materialize, or mutate +readiness to resolve the disagreement. + +## Implementation Boundary + +- Modify only the selected-lane count-resolution helper and focused tests. +- Do not edit `data/`, `outputs/`, source-rights decisions, thresholds, or + readiness calculations. +- Do not refactor unrelated Data Health presentation or navigation. +- Do not push, deploy, publish, tag, or release. + +## Verification + +Focused regressions must prove: + +- a conflicting project-status peer count cannot override a present saved + readiness peer count; +- a saved zero remains zero and cannot fall through to a larger project count; +- project status remains a fallback when saved readiness omits the count; +- missing counts remain unavailable rather than becoming factual zeroes; +- Personal and Operator selected peer-lane rendering use the same corrected + behavior; +- existing source-context and no-advice boundaries remain intact. + +Run the focused helper and route-render tests first. Run only the smallest +affected browser proof for the Personal and Operator peer routes unless the +final diff changes shared layout, navigation, or browser-gate code. From b85742ee0b18857f6fb6d624bb4aeadaf3bce73e Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Thu, 13 Aug 2026 20:06:42 -0400 Subject: [PATCH 03/13] Plan peer lane readiness precedence --- ...3-peer-lane-readiness-source-precedence.md | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md diff --git a/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md new file mode 100644 index 00000000..34bbf56b --- /dev/null +++ b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md @@ -0,0 +1,189 @@ +# Peer Lane Readiness Source Precedence Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make Personal and Operator selected Data Health lanes use current saved readiness counts before older project-status counts, without changing readiness data or operating context. + +**Architecture:** Keep the existing two inputs to `data_health_selected_lane_answer_cards()`. Change only its local `resolved_count()` precedence so parseable selected-profile saved counts win, including zero, while project status remains the fallback and continues to provide source-setup and next-step context. + +**Tech Stack:** Python 3.12, Streamlit, pytest, existing AppTest/render-smoke and browser-gate infrastructure. + +## Global Constraints + +- Data readiness first, analysis second, research decision last. +- Research-only: no recommendations, rankings, allocations, sizing, entry/exit guidance, transaction instructions, broker actions, performance claims, or fabricated data. +- Do not edit, regenerate, stage, reset, or clean any `data/` or `outputs/` path. +- Do not change readiness calculations, thresholds, source-rights decisions, navigation, or shared layout. +- No push, deployment, publication, tag, release, provider call, credential use, or external communication. + +--- + +### Task 1: Make saved readiness authoritative for selected-lane counts + +**Files:** +- Modify: `tests/test_dashboard_helpers.py` +- Modify: `src/dashboard.py:24856-24885` + +**Interfaces:** +- Consumes: `data_health_selected_lane_answer_cards(selected_lane_key, readiness_freshness, project_status_payload=None, saved_readiness_summary=None)`. +- Produces: unchanged list-of-card interface; only readiness-count source precedence changes. + +- [ ] **Step 1: Write the failing conflict and zero regressions** + +Replace the stale test that expects project-status aliases to override saved readiness with behavior tests using literal conflicting values: + +```python +def test_data_health_peer_lane_prefers_authoritative_saved_readiness_counts(): + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": {"tickers_peer_ready": 29, "data_gaps": 207} + }, + saved_readiness_summary={"peer_ready": 9, "blocked_by_data": 175}, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "9 tickers have trusted peer context" in rendered + assert "175 locked input row(s)" in rendered + assert "29 tickers have trusted peer context" not in rendered + assert "207 locked input row(s)" not in rendered + + +def test_data_health_peer_lane_treats_saved_zero_as_authoritative(): + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": {"tickers_peer_ready": 29, "data_gaps": 207} + }, + saved_readiness_summary={"peer_ready": 0, "blocked_by_data": 0}, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "0 tickers have trusted peer context" in rendered + assert "0 locked input row(s)" in rendered + assert "29 tickers have trusted peer context" not in rendered +``` + +The production mutation these tests catch is reversing the source order back to project-status-first or treating saved zero as absent. + +- [ ] **Step 2: Run the two tests and verify RED** + +Run: + +```bash +python3 -m pytest -q -p no:cacheprovider \ + tests/test_dashboard_helpers.py \ + -k 'authoritative_saved_readiness_counts or treats_saved_zero_as_authoritative' +``` + +Expected: both tests fail because the current helper renders the project-status values 29 and 207. + +- [ ] **Step 3: Implement the minimal precedence change** + +Change the nested resolver only: + +```python +def resolved_count(*keys: str) -> int | None: + saved_count = _summary_optional_count(saved_summary, *keys) + if saved_count is not None: + return saved_count + return _summary_optional_count(project_summary, *keys) +``` + +Do not alter source-setup counts, recommendations, lane copy, or the function signature. + +- [ ] **Step 4: Verify focused GREEN and existing fallbacks** + +Run: + +```bash +python3 -m pytest -q -p no:cacheprovider \ + tests/test_dashboard_helpers.py -k 'data_health_peer_lane or data_health_price_lane' +``` + +Expected: the new conflict/zero cases pass; missing saved readiness still falls back to project status; absent counts still render unavailable. + +--- + +### Task 2: Verify actual Personal and Operator route truth + +**Files:** +- Test: `tests/test_dashboard_render_smoke.py` +- Test: `tests/test_research_mode_dashboard_contract.py` +- Test: `tests/test_dashboard_helpers.py` +- Runtime evidence: fresh temporary AppTest/browser outputs outside the repository. + +**Interfaces:** +- Consumes: exact routes `/?mode=research&page=data-health&ticker=AVGO&lane=peers&drawer=proof` and `/?mode=operator&page=data-health&ticker=AVGO&lane=peers&drawer=proof`. +- Produces: current-byte evidence that both modes render 9 and reject stale 29 while preserving hierarchy, return path, mode, runtime, and no-advice boundaries. + +- [ ] **Step 1: Run the affected static/render tests** + +```bash +python3 -m pytest -q -p no:cacheprovider \ + tests/test_dashboard_helpers.py \ + tests/test_dashboard_render_smoke.py \ + tests/test_research_mode_dashboard_contract.py +``` + +Expected: all tests pass with only known third-party warnings. + +- [ ] **Step 2: Run a fresh two-route AppTest or browser proof** + +Use the existing route/render harness on the two exact peer URLs. Assert each rendered result contains: + +- `Selected Lane Answer — Peers`; +- `9 tickers have trusted peer context`; +- no `29 tickers have trusted peer context`; +- the correct Personal or Operator mode boundary; +- no exception or traceback; +- no recommendation, ranking, sizing, allocation, transaction, or performance claim. + +Store all runtime evidence under a fresh `/tmp/stock-peer-lane-readiness-*` directory. Do not write screenshots or reports into the repository. + +- [ ] **Step 3: Verify repository and artifact hygiene** + +```bash +git diff --check +git status --short +git diff --name-only f88c4cdcdbffbf65928671b3acbfa51f6b1cdf48...HEAD +``` + +Expected: only the design, plan, production helper, and focused test files are intentional; no `data/` or `outputs/` path is changed or staged. + +--- + +### Task 3: Independent review and local commit + +**Files:** +- Review: complete branch diff against `f88c4cdcdbffbf65928671b3acbfa51f6b1cdf48`. + +**Interfaces:** +- Consumes: final source/test diff and fresh verification artifacts. +- Produces: reviewer verdict with Critical/Important findings or READY. + +- [ ] **Step 1: Request independent read-only review** + +Ask the existing independent reviewer to inspect source precedence, zero semantics, fallback behavior, mode consistency, test quality, and protected-path hygiene. Do not edit while the review is active. + +- [ ] **Step 2: Resolve any Critical or Important finding test-first** + +For each reproduced finding, add a focused RED before changing production. Re-run only invalidated evidence. + +- [ ] **Step 3: Run final verification and commit named paths** + +After a READY verdict, run the final focused suite, `git diff --check`, and protected-path status verification. Stage exact named files only and commit locally. Do not push. From 92bd9646f005a7231a873fdc9a1d43db3fa0c2a6 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Thu, 13 Aug 2026 21:14:35 -0400 Subject: [PATCH 04/13] Honor saved readiness in peer lanes --- ...3-peer-lane-readiness-source-precedence.md | 20 +- ...lane-readiness-source-precedence-design.md | 11 +- src/dashboard.py | 27 ++- src/data_health_summary.py | 32 +++ tests/test_dashboard_helpers.py | 216 +++++++++++++++++- 5 files changed, 283 insertions(+), 23 deletions(-) diff --git a/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md index 34bbf56b..830727a4 100644 --- a/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md +++ b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md @@ -4,7 +4,7 @@ **Goal:** Make Personal and Operator selected Data Health lanes use current saved readiness counts before older project-status counts, without changing readiness data or operating context. -**Architecture:** Keep the existing two inputs to `data_health_selected_lane_answer_cards()`. Change only its local `resolved_count()` precedence so parseable selected-profile saved counts win, including zero, while project status remains the fallback and continues to provide source-setup and next-step context. +**Architecture:** Keep the existing two inputs to `data_health_selected_lane_answer_cards()`. Make `dashboard_readiness_summary()` record count-level source-column provenance, then make the local resolver prefer only parseable, evidence-backed selected-profile counts, including zero. Project status remains the fallback and continues to provide source-setup and next-step context. **Tech Stack:** Python 3.12, Streamlit, pytest, existing AppTest/render-smoke and browser-gate infrastructure. @@ -23,6 +23,7 @@ **Files:** - Modify: `tests/test_dashboard_helpers.py` - Modify: `src/dashboard.py:24856-24885` +- Modify: `src/data_health_summary.py:26-130` **Interfaces:** - Consumes: `data_health_selected_lane_answer_cards(selected_lane_key, readiness_freshness, project_status_payload=None, saved_readiness_summary=None)`. @@ -94,17 +95,28 @@ Expected: both tests fail because the current helper renders the project-status - [ ] **Step 3: Implement the minimal precedence change** -Change the nested resolver only: +Record the count keys whose source columns are present in +`dashboard_readiness_summary()` as `_count_evidence_keys`. Then change the +nested resolver so adapter-produced summaries use a saved count only when its +requested key is evidence-backed; explicit caller-supplied mappings without +metadata retain their current literal semantics: ```python def resolved_count(*keys: str) -> int | None: - saved_count = _summary_optional_count(saved_summary, *keys) + saved_count = ( + _summary_optional_count(saved_summary, *keys) + if saved_count_evidence is None + or any(key in saved_count_evidence for key in keys) + else None + ) if saved_count is not None: return saved_count return _summary_optional_count(project_summary, *keys) ``` -Do not alter source-setup counts, recommendations, lane copy, or the function signature. +Resolve `data_sources_*` directly from `project_summary`; do not route those +operating-context counts through the saved-readiness resolver. Do not alter +recommendations, lane copy, or the function signature. - [ ] **Step 4: Verify focused GREEN and existing fallbacks** diff --git a/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md index 1faec3b4..c4efbf6e 100644 --- a/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md +++ b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md @@ -22,8 +22,10 @@ readiness-owned count over a present saved readiness value. in this order: 1. Use the selected saved readiness summary when any requested canonical or - alias key is present and parseable. A saved value of zero is authoritative; - it must not be treated as missing. + alias key is present, parseable, and backed by the corresponding source + column. The summary adapter records count-level evidence keys so a missing + column cannot become authoritative zero. A source-backed saved value of zero + is authoritative; it must not be treated as missing. 2. Fall back to the saved project-status summary only when the selected saved readiness summary does not provide the requested count. 3. Report the count as unavailable when neither source provides it. @@ -49,7 +51,8 @@ readiness to resolve the disagreement. ## Implementation Boundary -- Modify only the selected-lane count-resolution helper and focused tests. +- Modify the selected-lane count-resolution helper, the readiness-summary + adapter that supplies count-level provenance, and focused tests only. - Do not edit `data/`, `outputs/`, source-rights decisions, thresholds, or readiness calculations. - Do not refactor unrelated Data Health presentation or navigation. @@ -62,6 +65,8 @@ Focused regressions must prove: - a conflicting project-status peer count cannot override a present saved readiness peer count; - a saved zero remains zero and cannot fall through to a larger project count; +- a nonempty saved report missing the requested count column falls back instead + of promoting its synthesized zero; - project status remains a fallback when saved readiness omits the count; - missing counts remain unavailable rather than becoming factual zeroes; - Personal and Operator selected peer-lane rendering use the same corrected diff --git a/src/dashboard.py b/src/dashboard.py index 79a6f85c..68f2dec8 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -24874,12 +24874,23 @@ def data_health_selected_lane_answer_cards( else {} ) saved_summary = dict(saved_readiness_summary or {}) + saved_count_evidence_value = saved_summary.get("_count_evidence_keys") + saved_count_evidence = ( + {str(key) for key in saved_count_evidence_value} + if isinstance(saved_count_evidence_value, (list, tuple, set, frozenset)) + else None + ) def resolved_count(*keys: str) -> int | None: - project_count = _summary_optional_count(project_summary, *keys) - if project_count is not None: - return project_count - return _summary_optional_count(saved_summary, *keys) + saved_count = ( + _summary_optional_count(saved_summary, *keys) + if saved_count_evidence is None + or any(key in saved_count_evidence for key in keys) + else None + ) + if saved_count is not None: + return saved_count + return _summary_optional_count(project_summary, *keys) recommended_rows = ( project_status_payload.get("recommended_next_command_rows", []) if isinstance(project_status_payload, dict) @@ -24972,10 +24983,10 @@ def resolved_count(*keys: str) -> int | None: ) freshness_status = saved_readiness_display_label(readiness_freshness.status) source_setup_note = "" - source_total = resolved_count("data_sources_total") - source_available = resolved_count("data_sources_available") - optional_locked = resolved_count("data_sources_optional_locked") - required_attention = resolved_count("data_sources_needing_attention") + source_total = _summary_optional_count(project_summary, "data_sources_total") + source_available = _summary_optional_count(project_summary, "data_sources_available") + optional_locked = _summary_optional_count(project_summary, "data_sources_optional_locked") + required_attention = _summary_optional_count(project_summary, "data_sources_needing_attention") source_counts = (source_total, source_available, optional_locked, required_attention) if any(count is not None for count in source_counts): if all(count is not None for count in source_counts): diff --git a/src/data_health_summary.py b/src/data_health_summary.py index 65374753..7317a37e 100644 --- a/src/data_health_summary.py +++ b/src/data_health_summary.py @@ -30,6 +30,37 @@ def dashboard_readiness_summary( analyst_readiness_frame: pd.DataFrame | None, ticker_readiness_frame: pd.DataFrame | None = None, ) -> dict[str, object]: + ticker_columns = ( + set(ticker_readiness_frame.columns) + if ticker_readiness_frame is not None and not ticker_readiness_frame.empty + else set() + ) + coverage_columns = ( + set(coverage_frame.columns) + if coverage_frame is not None and not coverage_frame.empty + else set() + ) + count_evidence_keys: set[str] = set() + if ticker_columns: + if "price_ready" in ticker_columns: + count_evidence_keys.add("price_ready") + if "fundamentals_ready" in ticker_columns: + count_evidence_keys.add("fundamentals_ready") + if "peer_ready" in ticker_columns: + count_evidence_keys.add("peer_ready") + else: + if coverage_columns.intersection({"has_prices", "price_ready"}): + count_evidence_keys.add("price_ready") + if "peer_ready" in coverage_columns: + count_evidence_keys.add("peer_ready") + if "dcf_ready" in ticker_columns or ( + dcf_readiness_frame is not None + and not dcf_readiness_frame.empty + and "is_dcf_ready" in dcf_readiness_frame.columns + ): + count_evidence_keys.add("dcf_ready") + if "overall_readiness_state" in ticker_columns: + count_evidence_keys.update({"blocked", "blocked_by_data"}) universe_count = 0 if coverage_frame is None or coverage_frame.empty else len(coverage_frame) master_count = 0 if ticker_readiness_frame is None or ticker_readiness_frame.empty else int(bool_series(ticker_readiness_frame, "in_master_universe").sum()) active_count = 0 if ticker_readiness_frame is None or ticker_readiness_frame.empty else int(bool_series(ticker_readiness_frame, "in_active_universe").sum()) @@ -124,6 +155,7 @@ def dashboard_readiness_summary( "excluded_count": excluded_count or dcf_excluded, "missing_credentials": missing_credentials, "configured_credentials": configured_credentials, + "_count_evidence_keys": sorted(count_evidence_keys), "updated_at": updated_at, "manual_import_paths": [ "Price import file folder: data/staged/prices/ -> make import-prices", diff --git a/tests/test_dashboard_helpers.py b/tests/test_dashboard_helpers.py index 800d1cc4..8b979ed7 100644 --- a/tests/test_dashboard_helpers.py +++ b/tests/test_dashboard_helpers.py @@ -29401,8 +29401,8 @@ def test_data_health_peer_lane_overlays_partial_project_status_on_saved_readines assert "0 tickers have trusted peer context" not in rendered -def test_data_health_peer_lane_prefers_newer_project_status_alias_counts(): - """Catches saved canonical keys winning over newer project-status aliases.""" +def test_data_health_peer_lane_prefers_authoritative_saved_readiness_counts(): + """Catches stale project status widening current saved readiness.""" cards = dashboard.data_health_selected_lane_answer_cards( "peers", @@ -29413,22 +29413,183 @@ def test_data_health_peer_lane_prefers_newer_project_status_alias_counts(): ), project_status_payload={ "summary": { - "tickers_peer_ready": 12, - "data_gaps": 5, + "tickers_peer_ready": 29, + "data_gaps": 207, } }, saved_readiness_summary={ "peer_ready": 9, - "blocked_by_data": 3276, + "blocked_by_data": 175, }, ) rendered = " ".join( str(value) for card in cards for value in card.values() ).lower() - assert "12 tickers have trusted peer context" in rendered - assert "5 locked input row(s)" in rendered - assert "9 tickers have trusted peer context" not in rendered + assert "9 tickers have trusted peer context" in rendered + assert "175 locked input row(s)" in rendered + assert "29 tickers have trusted peer context" not in rendered + assert "207 locked input row(s)" not in rendered + + +def test_data_health_peer_lane_treats_saved_zero_as_authoritative(): + """Catches zero saved readiness falling through to stale positive counts.""" + + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": { + "tickers_peer_ready": 29, + "data_gaps": 207, + } + }, + saved_readiness_summary={ + "master_count": 3538, + "peer_ready": 0, + "blocked_by_data": 0, + "updated_at": "2026-08-02T00:00:00Z", + }, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "0 tickers have trusted peer context" in rendered + assert "0 locked input row(s)" in rendered + assert "29 tickers have trusted peer context" not in rendered + + +def test_data_health_peer_lane_ignores_synthesized_zeroes_when_saved_evidence_is_missing(): + """Catches an absent saved report suppressing a valid project-status fallback.""" + + missing_saved_summary = dashboard.dashboard_readiness_summary( + None, + None, + None, + None, + None, + ) + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "missing", + "Saved readiness evidence is unavailable.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": { + "tickers_peer_ready": 29, + "data_gaps": 207, + } + }, + saved_readiness_summary=missing_saved_summary, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "29 tickers have trusted peer context" in rendered + assert "207 locked input row(s)" in rendered + assert "0 tickers have trusted peer context" not in rendered + assert "0 locked input row(s)" not in rendered + + +def test_data_health_peer_lane_ignores_zeroes_synthesized_from_missing_count_columns(): + """Catches aggregate evidence being mistaken for peer/state count evidence.""" + + incomplete_ticker_readiness = pd.DataFrame( + [ + { + "ticker": "AVGO", + "in_master_universe": True, + "updated_at": "2026-08-02T00:00:00Z", + } + ] + ) + incomplete_saved_summary = dashboard.dashboard_readiness_summary( + None, + None, + None, + None, + incomplete_ticker_readiness, + ) + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": { + "tickers_peer_ready": 29, + "data_gaps": 207, + } + }, + saved_readiness_summary=incomplete_saved_summary, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "29 tickers have trusted peer context" in rendered + assert "207 locked input row(s)" in rendered + assert "0 tickers have trusted peer context" not in rendered + assert "0 locked input row(s)" not in rendered + + +def test_data_health_peer_lane_falls_back_when_ticker_report_omits_count_column(): + """Catches an unrelated coverage frame widening an incomplete saved report.""" + + coverage = pd.DataFrame( + [ + { + "ticker": "AVGO", + "has_prices": True, + "usable_for_momentum": True, + "peer_ready": True, + } + ] + ) + incomplete_ticker_readiness = pd.DataFrame( + [ + { + "ticker": "AVGO", + "in_master_universe": True, + "updated_at": "2026-08-02T00:00:00Z", + } + ] + ) + saved_summary = dashboard.dashboard_readiness_summary( + coverage, + None, + None, + None, + incomplete_ticker_readiness, + ) + assert saved_summary["momentum_ready"] == 0 + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={"summary": {"tickers_peer_ready": 29}}, + saved_readiness_summary=saved_summary, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "29 tickers have trusted peer context" in rendered + assert "0 tickers have trusted peer context" not in rendered + assert "1 tickers have trusted peer context" not in rendered def test_data_health_peer_lane_falls_back_when_project_summary_shape_is_invalid(): @@ -29455,6 +29616,45 @@ def test_data_health_peer_lane_falls_back_when_project_summary_shape_is_invalid( assert "175 locked input row(s)" in rendered +def test_data_health_source_setup_counts_remain_project_status_only(): + """Catches readiness precedence leaking into project-status source context.""" + + cards = dashboard.data_health_selected_lane_answer_cards( + "peers", + dashboard.FreshnessStatus( + "current", + "Saved readiness artifacts are current.", + "make readiness-preview TOP_N=20", + ), + project_status_payload={ + "summary": { + "data_sources_total": 10, + "data_sources_available": 7, + "data_sources_optional_locked": 3, + "data_sources_needing_attention": 0, + "tickers_peer_ready": 29, + "data_gaps": 207, + } + }, + saved_readiness_summary={ + "peer_ready": 9, + "blocked_by_data": 175, + "data_sources_total": 99, + "data_sources_available": 98, + "data_sources_optional_locked": 1, + "data_sources_needing_attention": 4, + }, + ) + rendered = " ".join( + str(value) for card in cards for value in card.values() + ).lower() + + assert "source setup: 7/10 data sources available" in rendered + assert "3 optional provider gap(s)" in rendered + assert "0 required gap(s)" in rendered + assert "98/99 data sources available" not in rendered + + def test_data_health_peer_lane_does_not_invent_absent_source_status_zeroes(): """Catches a partial source summary being completed with unsupported zeroes.""" From 00868439d403661e2c6c8f7c973b1960eb8b8bd0 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Thu, 13 Aug 2026 22:12:59 -0400 Subject: [PATCH 05/13] Clarify mapped peer trend readiness --- ...3-peer-lane-readiness-source-precedence.md | 12 +++--- ...lane-readiness-source-precedence-design.md | 8 +++- src/dashboard.py | 4 +- tests/test_dashboard_helpers.py | 37 ++++++++++--------- tests/test_dashboard_render_smoke.py | 7 ++-- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md index 830727a4..665ceeeb 100644 --- a/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md +++ b/docs/superpowers/plans/2026-08-13-peer-lane-readiness-source-precedence.md @@ -51,9 +51,9 @@ def test_data_health_peer_lane_prefers_authoritative_saved_readiness_counts(): str(value) for card in cards for value in card.values() ).lower() - assert "9 tickers have trusted peer context" in rendered + assert "9 tickers have mapped peer trend context" in rendered assert "175 locked input row(s)" in rendered - assert "29 tickers have trusted peer context" not in rendered + assert "29 tickers have mapped peer trend context" not in rendered assert "207 locked input row(s)" not in rendered @@ -74,9 +74,9 @@ def test_data_health_peer_lane_treats_saved_zero_as_authoritative(): str(value) for card in cards for value in card.values() ).lower() - assert "0 tickers have trusted peer context" in rendered + assert "0 tickers have mapped peer trend context" in rendered assert "0 locked input row(s)" in rendered - assert "29 tickers have trusted peer context" not in rendered + assert "29 tickers have mapped peer trend context" not in rendered ``` The production mutation these tests catch is reversing the source order back to project-status-first or treating saved zero as absent. @@ -159,8 +159,8 @@ Expected: all tests pass with only known third-party warnings. Use the existing route/render harness on the two exact peer URLs. Assert each rendered result contains: - `Selected Lane Answer — Peers`; -- `9 tickers have trusted peer context`; -- no `29 tickers have trusted peer context`; +- `9 tickers have mapped peer trend context`; +- no `29 tickers have mapped peer trend context`; - the correct Personal or Operator mode boundary; - no exception or traceback; - no recommendation, ranking, sizing, allocation, transaction, or performance claim. diff --git a/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md index c4efbf6e..2e74353a 100644 --- a/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md +++ b/docs/superpowers/specs/2026-08-13-peer-lane-readiness-source-precedence-design.md @@ -7,7 +7,7 @@ The selected Data Health peer lane can display a stale count from `outputs/project_status.json` even when the selected profile's saved readiness summary contains a newer authoritative peer-readiness count. The current local -case renders `29 tickers have trusted peer context` from an older 3,541-row +case renders `29 tickers have mapped peer trend context` from an older 3,541-row project-status snapshot while the saved readiness report records 9 peer-ready rows across 3,538 tickers. @@ -44,6 +44,12 @@ Candidate peers remain context only, and the answer must continue to avoid recommendation, ranking, allocation, sizing, transaction, or performance language. +The visible label is deliberately **mapped peer trend context**, not “trusted +peer context.” The `peer_ready` field reflects mapped-price trend readiness; +it does not prove a genuinely reviewed peer relationship or unlock peer +valuation. Those gates remain independent and withheld until their direct +evidence exists. + If saved readiness is missing, the route may use a complete available project-status count. If both are missing or malformed, it must fail closed to an unavailable count. No route may refresh, rebuild, materialize, or mutate diff --git a/src/dashboard.py b/src/dashboard.py index 68f2dec8..44ac4924 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -25010,9 +25010,9 @@ def resolved_count(*keys: str) -> int | None: else "DCF-ready and fundamentals-ready counts are unavailable; source-backed company inputs stay withheld until saved evidence is available." ) peer_lane_answer = ( - f"{peer_ready:,} tickers have trusted peer context." + f"{peer_ready:,} tickers have mapped peer trend context." if peer_ready is not None - else "Trusted peer count is unavailable." + else "Mapped peer trend count is unavailable." ) locked_input_answer = ( f"{data_gaps:,} locked input row(s) remain visible instead of being inferred." diff --git a/tests/test_dashboard_helpers.py b/tests/test_dashboard_helpers.py index 8b979ed7..06677b14 100644 --- a/tests/test_dashboard_helpers.py +++ b/tests/test_dashboard_helpers.py @@ -29365,9 +29365,9 @@ def test_data_health_peer_lane_uses_saved_readiness_counts_when_project_status_i str(value) for card in cards for value in card.values() ).lower() - assert "9 tickers have trusted peer context" in rendered + assert "9 tickers have mapped peer trend context" in rendered assert "175 locked input row(s)" in rendered - assert "0 tickers have trusted peer context" not in rendered + assert "trusted peer context" not in rendered assert "freshness: current for saved sources" in rendered @@ -29396,9 +29396,9 @@ def test_data_health_peer_lane_overlays_partial_project_status_on_saved_readines str(value) for card in cards for value in card.values() ).lower() - assert "9 tickers have trusted peer context" in rendered + assert "9 tickers have mapped peer trend context" in rendered assert "3,276 locked input row(s)" in rendered - assert "0 tickers have trusted peer context" not in rendered + assert "0 tickers have mapped peer trend context" not in rendered def test_data_health_peer_lane_prefers_authoritative_saved_readiness_counts(): @@ -29426,9 +29426,9 @@ def test_data_health_peer_lane_prefers_authoritative_saved_readiness_counts(): str(value) for card in cards for value in card.values() ).lower() - assert "9 tickers have trusted peer context" in rendered + assert "9 tickers have mapped peer trend context" in rendered assert "175 locked input row(s)" in rendered - assert "29 tickers have trusted peer context" not in rendered + assert "29 tickers have mapped peer trend context" not in rendered assert "207 locked input row(s)" not in rendered @@ -29459,9 +29459,9 @@ def test_data_health_peer_lane_treats_saved_zero_as_authoritative(): str(value) for card in cards for value in card.values() ).lower() - assert "0 tickers have trusted peer context" in rendered + assert "0 tickers have mapped peer trend context" in rendered assert "0 locked input row(s)" in rendered - assert "29 tickers have trusted peer context" not in rendered + assert "29 tickers have mapped peer trend context" not in rendered def test_data_health_peer_lane_ignores_synthesized_zeroes_when_saved_evidence_is_missing(): @@ -29493,9 +29493,9 @@ def test_data_health_peer_lane_ignores_synthesized_zeroes_when_saved_evidence_is str(value) for card in cards for value in card.values() ).lower() - assert "29 tickers have trusted peer context" in rendered + assert "29 tickers have mapped peer trend context" in rendered assert "207 locked input row(s)" in rendered - assert "0 tickers have trusted peer context" not in rendered + assert "0 tickers have mapped peer trend context" not in rendered assert "0 locked input row(s)" not in rendered @@ -29537,9 +29537,9 @@ def test_data_health_peer_lane_ignores_zeroes_synthesized_from_missing_count_col str(value) for card in cards for value in card.values() ).lower() - assert "29 tickers have trusted peer context" in rendered + assert "29 tickers have mapped peer trend context" in rendered assert "207 locked input row(s)" in rendered - assert "0 tickers have trusted peer context" not in rendered + assert "0 tickers have mapped peer trend context" not in rendered assert "0 locked input row(s)" not in rendered @@ -29587,9 +29587,9 @@ def test_data_health_peer_lane_falls_back_when_ticker_report_omits_count_column( str(value) for card in cards for value in card.values() ).lower() - assert "29 tickers have trusted peer context" in rendered - assert "0 tickers have trusted peer context" not in rendered - assert "1 tickers have trusted peer context" not in rendered + assert "29 tickers have mapped peer trend context" in rendered + assert "0 tickers have mapped peer trend context" not in rendered + assert "1 tickers have mapped peer trend context" not in rendered def test_data_health_peer_lane_falls_back_when_project_summary_shape_is_invalid(): @@ -29612,7 +29612,7 @@ def test_data_health_peer_lane_falls_back_when_project_summary_shape_is_invalid( str(value) for card in cards for value in card.values() ).lower() - assert "9 tickers have trusted peer context" in rendered + assert "9 tickers have mapped peer trend context" in rendered assert "175 locked input row(s)" in rendered @@ -29703,9 +29703,10 @@ def test_data_health_peer_lane_does_not_turn_absent_counts_into_factual_zeroes() str(value) for card in cards for value in card.values() ).lower() - assert "trusted peer count is unavailable" in rendered + assert "mapped peer trend count is unavailable" in rendered + assert "trusted peer count" not in rendered assert "locked input count is unavailable" in rendered - assert "0 tickers have trusted peer context" not in rendered + assert "0 tickers have mapped peer trend context" not in rendered assert "0 locked input row(s)" not in rendered diff --git a/tests/test_dashboard_render_smoke.py b/tests/test_dashboard_render_smoke.py index ae82e641..49c8d489 100644 --- a/tests/test_dashboard_render_smoke.py +++ b/tests/test_dashboard_render_smoke.py @@ -66,7 +66,7 @@ def test_operator_peer_lane_uses_the_same_saved_counts_as_personal_data_health() operator_result, personal_result = render_public_routes(Path("."), routes=routes) operator_rendered = "\n".join(operator_result.rendered_blocks) personal_rendered = "\n".join(personal_result.rendered_blocks) - peer_count_pattern = re.compile(r"([1-9][0-9,]*) tickers have trusted peer context") + peer_count_pattern = re.compile(r"([1-9][0-9,]*) tickers have mapped peer trend context") locked_count_pattern = re.compile(r"([1-9][0-9,]*) locked input rows? remain visible") operator_peer_count = peer_count_pattern.search(operator_rendered) personal_peer_count = peer_count_pattern.search(personal_rendered) @@ -83,7 +83,8 @@ def test_operator_peer_lane_uses_the_same_saved_counts_as_personal_data_health() assert operator_count is not None assert personal_count is not None assert operator_count.group(1) == personal_count.group(1) - assert "0 tickers have trusted peer context" not in operator_rendered + assert "0 tickers have mapped peer trend context" not in operator_rendered + assert "trusted peer context" not in operator_rendered.lower() assert "0 locked input row(s)" not in operator_rendered @@ -101,7 +102,7 @@ def test_public_peer_lane_arrival_renders_the_promised_selected_answer(): ("lane", "peers"), ("drawer", "proof"), ), - required_markers=("Selected Lane Answer", "9 tickers have trusted peer context"), + required_markers=("Selected Lane Answer", "9 tickers have mapped peer trend context"), ) result = render_public_routes(Path("."), routes=(route,))[0] From cc6dc1cb89ac8ea148153430f6cd0fcd9241717b Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 08:32:22 -0400 Subject: [PATCH 06/13] Keep public roadmap release notes clean --- ROADMAP.md | 2 +- tests/test_public_v1_release_docs.py | 9 ++++----- tests/test_public_wording_check.py | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 3a78c835..04d8e912 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -64,7 +64,7 @@ Stage A-G labels are continuation maturity lanes only; they do not replace the n Company Workbench HTML Research Brief — Historical pre-fix evidence: Task 4 local matrix completed at `c8c313b9c`. Modal modifiers and active exposure fail closed. Broad-review repairs must be evaluated only through direct current-head local and exact-head CI evidence; their presence alone establishes neither gate. Exact-head repair evidence: commit `b69badfc80424d3a97fae5f77706aa6ed1533167` passed the 5,828-test full suite, the required dashboard, render, HTML, accessibility, public, and hygiene gates, branch/PR synchronization, and exact-head GitHub Actions run `30726301045`. The brief downloads an immutable offline view of existing saved evidence and prepared Python scenario math, preserves independent field gates and research-only wording, writes no repository artifact, and does not activate readiness or create a new calculation engine. Pilot packaging remains blocked on readiness freshness and source proof. Source rights, current data, hosted operation, human and screen-reader accessibility, independent workflow sessions, screening validation, and probability calibration remain open gates. Local engineering evidence does not establish source rights, current-market data, readiness activation, a new or professional line-item model, hosted operation, human or screen-reader conformance, independent validation, market fit, screening alpha, or probability calibration. ## Next: Ordered Maturity Work -PR #113 merged the verified engineering closure into `main`; exact-head CI run `31704776477` passed. Do not rerun or resynchronize that completed slice without changed product bytes or separate owner authorization. Select the first incomplete safe roadmap priority. If the next gate needs an unavailable source, account, environment, reviewer, or elapsed event history, classify it once under **Externally blocked** and continue to the next executable priority. Passing local tests never completes an external gate. Evidence publication, snapshot, and retrieval timestamps must all be at or before the cutoff. +The prior engineering closure is incorporated in the default branch, and its automated engineering checks passed. Do not repeat that completed engineering slice without changed product bytes or separate owner authorization. Select the first incomplete safe roadmap priority. If the next gate needs an unavailable source, account, environment, reviewer, or elapsed event history, classify it once under **Externally blocked** and continue to the next executable priority. Passing local tests never completes an external gate. Evidence publication, snapshot, and retrieval timestamps must all be at or before the cutoff. Documentation and routing now name Personal Research as the root default, Public and Operator as explicit modes, and legacy utilities as Operator-only compatibility surfaces. Reopen this contract when current repository evidence reproduces drift. diff --git a/tests/test_public_v1_release_docs.py b/tests/test_public_v1_release_docs.py index e66f65b7..941625fe 100644 --- a/tests/test_public_v1_release_docs.py +++ b/tests/test_public_v1_release_docs.py @@ -2328,9 +2328,8 @@ def test_broad_review_docs_use_durable_release_routing_and_fail_closed_boundarie for document in (readme,): assert "Complete the direct local matrix and current-head local evidence first" in document assert "Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization" in document - assert "PR #113 merged the verified engineering closure into `main`" in roadmap - assert "exact-head CI run `31704776477` passed" in roadmap - assert "Do not rerun or resynchronize that completed slice" in roadmap + assert "The prior engineering closure is incorporated in the default branch" in roadmap + assert "Do not repeat that completed engineering slice" in roadmap assert "Complete the direct local matrix and current-head local evidence first" not in roadmap assert "The Calm Institutional Workspace local engineering closure is complete" in continuation assert "49123a989dae263e8c125ad3032bf96d0107853d" in continuation @@ -2499,8 +2498,8 @@ def test_workspace_modernization_docs_share_one_default_and_explicit_mode_contra assert release_first_claim not in roadmap assert "Complete the direct local matrix and current-head local evidence first" in readme assert "Remote synchronization, draft-PR updates, and exact-head CI require separate owner authorization" in readme - assert "PR #113 merged the verified engineering closure into `main`" in roadmap - assert "Do not rerun or resynchronize that completed slice" in roadmap + assert "The prior engineering closure is incorporated in the default branch" in roadmap + assert "Do not repeat that completed engineering slice" in roadmap assert "Sidebar navigation remains the single public route chooser" not in dashboard_qa assert "in-content public workflow navigation is the route chooser" in dashboard_qa for stale_claim in ( diff --git a/tests/test_public_wording_check.py b/tests/test_public_wording_check.py index bba67927..5d917c21 100644 --- a/tests/test_public_wording_check.py +++ b/tests/test_public_wording_check.py @@ -98,6 +98,15 @@ def test_public_wording_scan_scope_is_public_but_not_tests_or_generated_csvs(): assert "data/reports/ticker_readiness_report.csv" not in paths +def test_public_wording_repository_scan_has_no_forbidden_public_copy(): + module = load_public_wording_module() + + scanned_count, matches = module.scan_public_files(Path(".")) + + assert scanned_count > 0 + assert matches == [], module.build_report(scanned_count, matches) + + def test_dashboard_preview_asset_uses_three_public_paths_in_order(): svg = Path("docs/assets/dashboard-preview.svg").read_text(encoding="utf-8") From a0c822e530691235603f6c45b9614f0e0f00bd7c Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:03:13 -0400 Subject: [PATCH 07/13] Design product polish truth fixes --- ...08-14-product-polish-truth-fixes-design.md | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-14-product-polish-truth-fixes-design.md diff --git a/docs/superpowers/specs/2026-08-14-product-polish-truth-fixes-design.md b/docs/superpowers/specs/2026-08-14-product-polish-truth-fixes-design.md new file mode 100644 index 00000000..b787048a --- /dev/null +++ b/docs/superpowers/specs/2026-08-14-product-polish-truth-fixes-design.md @@ -0,0 +1,133 @@ +# Product Polish And Truth Fixes Design + +**Status:** Approved direction for bounded local implementation; no push, +deployment, generated-artifact mutation, or product-scope expansion is +authorized. + +## Objective + +Turn the current live audit into three small repairs in the existing calm +institutional workspace: + +1. keep the Research Desk supporting-evidence explanation readable at desktop + widths; +2. describe saved peer coverage without implying reviewed peer-valuation + evidence; and +3. prevent missing provider-status evidence from being presented as a + configured provider. + +The existing Streamlit product is the prototype. The work does not introduce a +parallel mock application, new route, new workflow, new data source, or new +visual language. + +## Considered Approaches + +### A. Targeted root-cause repairs — selected + +Keep the existing information architecture and component system. Scope the +layout correction to the Research Desk evidence row, change the Public Home +metric label to the product's established mapped-peer terminology, and make +project status fail closed when saved provider evidence is unavailable. + +This is the smallest approach that repairs what users actually see without +changing readiness calculations or widening the product. + +### B. Copy-only workaround — rejected + +Shorten the Research Desk freshness warning until it happens to fit, and leave +the shared grid unchanged. This would hide the current symptom while another +long translated label could reproduce it. + +### C. Broad visual/status refactor — rejected + +Redesign all evidence rows and consolidate every status command around a new +state model. This would create unnecessary regression risk across Public, +Personal Research, and Operator modes. + +## Design Decisions + +### Research Desk supporting evidence + +The current three-column row allows the unbounded freshness message in the +middle column to consume the available width and collapse the reason paragraph +to zero pixels. The Research Desk row will use a bounded two-column reading +pattern: + +- lane and semantic state on the left; +- freshness message and explanation on the right; +- the explanation starts on its own line; and +- at phone width, the existing single-column stack remains. + +The change is scoped to `.research-desk-brief` so other dense evidence tables +retain their established layout. Text remains complete, selectable, and in DOM +order; no truncation, ellipsis, line clamp, nested scrolling, or hidden copy is +allowed. Because the scoped desktop selector is more specific than the generic +phone rule, the `640px` media query must include its own scoped reset to a +single column with normal grid placement. + +### Public Home peer metric + +The saved `peer_ready` count measures mapped peer price/trend context. It does +not prove an independently reviewed peer relationship or trusted peer-relative +valuation inputs. Public Home will label the metric **Mapped peer trend**. + +The count and calculation remain unchanged. Data Health continues to explain +that peer valuation is independently gated and withheld when its direct +evidence is absent. + +### Provider-status truth + +`outputs/session_source_preflight.json` is optional saved evidence. Its absence +must not imply that FMP is configured. + +When the saved operator summary is unavailable or malformed, the FMP stage will +be classified as `source_status_review_required`, with evidence that provider +configuration is not established from saved status and a read-only next action +to run `make provider-setup-checklist`. Project status describes only the saved +session-source evidence; the checklist describes current local key setup. It +will not claim either that a key is configured or that a secret is definitely +absent. + +When saved evidence explicitly lists FMP as needing setup, the existing +`awaiting_external_setup` result remains. When saved evidence explicitly shows +that FMP is configured, the existing reviewed one-ticker smoke boundary +remains. Secret values are never printed. + +## Error And Boundary Behavior + +- Long evidence text wraps normally and cannot collapse the adjacent reason. +- Missing or malformed provider status fails closed to review-required. +- An explicit configured or missing provider state retains its current path. +- No provider probe, network call, refresh, import, apply, materialization, or + generated-file write is added. +- No peer count, readiness threshold, source-rights state, recommendation, or + ranking changes. + +## Verification + +Test-first regressions must prove: + +- the Research Desk desktop evidence row reserves usable width for the reason, + retains full text, and still stacks at phone width; +- Public Home renders `Mapped peer trend` and never renders `Trusted peers`; +- absent or malformed saved source status cannot produce + `configured_smoke_required` or `FMP_API_KEY appears configured`; +- explicit missing and configured saved states retain their established + behavior; and +- `make project-status-check` and `make next-stage` no longer contradict each + other in the current no-provider-evidence state. + +After focused GREEN tests, capture matching before/after Research Desk desktop, +Public Home desktop, and Research Desk phone screenshots. Measure the desktop +reason width and row height in the live DOM. Run only the smallest affected +browser routes unless a shared selector or browser-gate contract changes. Ask +for independent review before any local commit beyond the design record. + +## Explicit Non-Goals + +- no visual redesign or Figma artifact; +- no new dashboard, indicator, recommendation, or analytical function; +- no provider configuration, source activation, refresh, or data mutation; +- no hosted deployment, publishing, PR update, merge, or push; and +- no claim of WCAG, screen-reader, human-keyboard, or production readiness from + automated evidence alone. From 3770d097119f97829b4546bf227f716d963b5dab Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:10:38 -0400 Subject: [PATCH 08/13] Plan product polish truth fixes --- .../2026-08-14-product-polish-truth-fixes.md | 453 ++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-14-product-polish-truth-fixes.md diff --git a/docs/superpowers/plans/2026-08-14-product-polish-truth-fixes.md b/docs/superpowers/plans/2026-08-14-product-polish-truth-fixes.md new file mode 100644 index 00000000..093b46a8 --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-product-polish-truth-fixes.md @@ -0,0 +1,453 @@ +# Product Polish And Truth Fixes Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Repair one Research Desk layout failure and two user-facing truth contradictions without changing readiness calculations, generated data, routes, or product scope. + +**Architecture:** Keep the existing calm institutional visual system and route helpers. Add a Research Desk-specific evidence-row layout override, correct one Public Home metric label, and make the existing project-status stage classifier distinguish recorded provider status from unavailable saved evidence. + +**Tech Stack:** Python 3, Streamlit HTML helpers, CSS emitted by `dashboard_visual_system_css()`, pytest, the existing workspace visual browser gate, Chrome. + +## Global Constraints + +- Preserve the research-only boundary and “data readiness first, analysis second, research decision last.” +- Do not edit `data/`, `outputs/`, readiness calculations, source-rights decisions, or thresholds. +- Do not add provider probes, network calls, imports, refreshes, applies, materialization, or secret output. +- Do not truncate, clamp, hide, or nest-scroll the Research Desk evidence copy. +- Use `Mapped peer trend`; do not imply reviewed peer relationships or peer-relative valuation readiness. +- Missing or malformed saved provider status must fail closed to review-required. +- Do not push, deploy, publish, merge, or update PR #114. +- Stage only named intentional files; never use `git add -A`. + +--- + +### Task 1: Research Desk supporting-evidence layout + +**Files:** +- Modify: `src/dashboard_visual_system.py:694-708, 879-930` +- Test: `tests/test_dashboard_visual_system.py:150-166` +- Test: `tests/test_research_workspace.py:1549-1570` + +**Interfaces:** +- Consumes: `dashboard_visual_system_css() -> str` and the existing `.research-desk-brief .sr-evidence-row` DOM emitted by `research_desk_brief_html()`. +- Produces: a scoped two-column desktop layout and a scoped one-column phone reset; no Python API changes. + +- [ ] **Step 1: Write the failing CSS and full-copy regression** + +Add this independent CSS contract to `tests/test_dashboard_visual_system.py`: + +```python +def test_research_desk_evidence_layout_reserves_reason_width_and_resets_on_phone(): + css = visual.dashboard_visual_system_css() + desktop = css[ + css.index(".research-desk-brief .sr-evidence-row {") : + css.index(".sr-status-chip {") + ] + mobile = css[css.index("@media (max-width: 640px)") :] + + assert "grid-template-columns: minmax(12rem, .75fr) minmax(0, 2fr)" in desktop + assert ".research-desk-brief .sr-evidence-count" in desktop + assert "overflow-wrap: anywhere" in desktop + assert ".research-desk-brief .sr-evidence-row p" in desktop + assert "grid-column: 2" in desktop + assert ".research-desk-brief .sr-evidence-row" in mobile + assert "grid-template-columns: 1fr" in mobile + assert "grid-column: 1" in mobile + assert "grid-row: auto" in mobile +``` + +Extend `test_research_desk_brief_and_advanced_evidence_html_stay_answer_first_and_command_free()` with these hand-derived literal assertions: + +```python +assert "Saved readiness is current." in desk_html +assert "No unresolved saved source-change item is available." in desk_html +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q \ + tests/test_dashboard_visual_system.py::test_research_desk_evidence_layout_reserves_reason_width_and_resets_on_phone \ + tests/test_research_workspace.py::test_research_desk_brief_and_advanced_evidence_html_stay_answer_first_and_command_free +``` + +Expected: the new CSS contract fails because no scoped Research Desk layout exists; the existing DOM/full-copy assertion remains green. + +- [ ] **Step 3: Add the minimum scoped CSS** + +Add immediately after the generic `.sr-evidence-count` rule: + +```css +.research-desk-brief .sr-evidence-row { + grid-template-columns: minmax(12rem, .75fr) minmax(0, 2fr); + align-items: start; +} +.research-desk-brief .sr-evidence-lane { grid-row: 1 / span 2; } +.research-desk-brief .sr-evidence-count { + grid-column: 2; + overflow-wrap: anywhere; + white-space: normal; +} +.research-desk-brief .sr-evidence-row p { grid-column: 2; } +``` + +Inside the existing `@media (max-width: 640px)` block, after the generic evidence-row reset, add: + +```css +.research-desk-brief .sr-evidence-row { grid-template-columns: 1fr; } +.research-desk-brief .sr-evidence-lane, +.research-desk-brief .sr-evidence-count, +.research-desk-brief .sr-evidence-row p { + grid-column: 1; + grid-row: auto; +} +``` + +- [ ] **Step 4: Run focused GREEN** + +Run the Step 2 command. Expected: `2 passed` and no new warnings. + +- [ ] **Step 5: Review Task 1 diff without committing** + +Run: + +```bash +git diff --check +git diff -- src/dashboard_visual_system.py tests/test_dashboard_visual_system.py tests/test_research_workspace.py +``` + +Confirm the generic evidence-row contract and all phone navigation rules remain unchanged. + +--- + +### Task 2: Public Home peer wording + +**Files:** +- Modify: `src/dashboard.py:6653-6683` +- Test: `tests/test_dashboard_helpers.py:31272-31288` + +**Interfaces:** +- Consumes: `public_home_overview_html(summary: dict[str, object]) -> str` and its existing `peer_ready` value. +- Produces: the same count under the label `Mapped peer trend`; no calculation or payload change. + +- [ ] **Step 1: Write the failing rendered-copy regression** + +Extend `test_public_home_overview_keeps_one_start_action_and_compact_readiness_snapshot()`: + +```python +assert "
Mapped peer trend
29
" in html +assert "Trusted peers" not in html +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q \ + tests/test_dashboard_helpers.py::test_public_home_overview_keeps_one_start_action_and_compact_readiness_snapshot +``` + +Expected: FAIL because the rendered label is `Trusted peers`. + +- [ ] **Step 3: Replace only the visible metric label** + +In `public_home_overview_html()`, change: + +```python +f"
Trusted peers
{peer_ready:,}
" +``` + +to: + +```python +f"
Mapped peer trend
{peer_ready:,}
" +``` + +- [ ] **Step 4: Run focused GREEN** + +Run the Step 2 command. Expected: `1 passed`. + +- [ ] **Step 5: Review Task 2 diff without committing** + +Run: + +```bash +git diff --check +git diff -- src/dashboard.py tests/test_dashboard_helpers.py +``` + +Confirm the count source, count formatting, route, stop rule, and action remain unchanged. + +--- + +### Task 3: Fail-closed provider-status classification + +**Files:** +- Modify: `src/project_status.py:607-703` +- Test: `tests/test_project_status.py:1172-1225, 1370-1425` + +**Interfaces:** +- Consumes: `_remaining_public_stage_rows(summary, source_operator_summary=...) -> list[dict[str, str]]`. +- Produces: the existing FMP stage row with one additional state, `source_status_review_required`, when `needs_setup` evidence is absent or malformed. + +- [ ] **Step 1: Write missing, malformed, missing-key, and configured-state tests** + +Add a small literal `summary` fixture in each test or a local helper that returns only the hand-written counts required by `_remaining_public_stage_rows()`. + +```python +@pytest.mark.parametrize( + "source_operator_summary", + (None, {}, {"needs_setup": "fmp"}), +) +def test_project_status_fmp_stage_fails_closed_without_recorded_provider_state( + source_operator_summary, +): + rows = project_status._remaining_public_stage_rows( + { + "tickers_total": 10, + "tickers_with_prices": 2, + "tickers_usable_for_momentum": 2, + "tickers_fundamentals_ready": 1, + "tickers_dcf_ready": 1, + "tickers_peer_ready": 0, + "data_gaps": 8, + "data_sources_optional_locked": 3, + }, + source_operator_summary=source_operator_summary, + git_status_line="## main...origin/main", + ) + stage = next(row for row in rows if row["Stage"] == "FMP provider activation") + + assert stage["State"] == "source_status_review_required" + assert stage["Diagnostic State"] == "source_status_unavailable" + assert "not established from saved session status" in stage["Evidence"] + assert stage["Next Action"] == "Run make provider-setup-checklist to inspect current local setup." + assert "appears configured" not in " ".join(stage.values()) + + +@pytest.mark.parametrize( + ("needs_setup", "expected_state", "expected_diagnostic"), + ( + (["fmp", "alpha_vantage", "finnhub"], "awaiting_external_setup", "external_key_required"), + (["alpha_vantage", "finnhub"], "configured_smoke_required", "configured_smoke_required"), + ([], "configured_smoke_required", "configured_smoke_required"), + ), +) +def test_project_status_fmp_stage_preserves_explicit_saved_provider_states( + needs_setup, + expected_state, + expected_diagnostic, +): + rows = project_status._remaining_public_stage_rows( + { + "tickers_total": 10, + "tickers_with_prices": 2, + "tickers_usable_for_momentum": 2, + "tickers_fundamentals_ready": 1, + "tickers_dcf_ready": 1, + "tickers_peer_ready": 0, + "data_gaps": 8, + "data_sources_optional_locked": 3, + }, + source_operator_summary={"needs_setup": needs_setup}, + git_status_line="## main...origin/main", + ) + stage = next(row for row in rows if row["Stage"] == "FMP provider activation") + + assert stage["State"] == expected_state + assert stage["Diagnostic State"] == expected_diagnostic +``` + +- [ ] **Step 2: Run the focused tests and verify RED** + +Run: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q \ + tests/test_project_status.py::test_project_status_fmp_stage_fails_closed_without_recorded_provider_state \ + tests/test_project_status.py::test_project_status_fmp_stage_preserves_explicit_saved_provider_states +``` + +Expected: the unavailable/malformed cases fail because they are currently classified as configured; explicit-state cases pass. + +- [ ] **Step 3: Implement the recorded-status boundary** + +Before normalizing `needs_setup`, retain the raw value and require the canonical list shape: + +```python +source_operator_summary = ( + source_operator_summary if isinstance(source_operator_summary, dict) else {} +) +raw_needs_setup = source_operator_summary.get("needs_setup") +provider_status_recorded = isinstance(raw_needs_setup, list) +needs_setup = [ + str(item).strip().lower() + for item in (raw_needs_setup if provider_status_recorded else []) + if str(item).strip() +] +``` + +Build the FMP row before `rows`: + +```python +if not provider_status_recorded: + fmp_stage = { + "State": "source_status_review_required", + "Diagnostic State": "source_status_unavailable", + "Evidence": "FMP configuration is not established from saved session status.", + "Next Action": "Run make provider-setup-checklist to inspect current local setup.", + } +elif fmp_missing: + fmp_stage = { + "State": "awaiting_external_setup", + "Diagnostic State": "external_key_required", + "Evidence": "FMP_API_KEY is not configured in the saved session source status.", + "Next Action": "Set FMP_API_KEY outside the repo, then run one reviewed ticker smoke.", + } +else: + fmp_stage = { + "State": "configured_smoke_required", + "Diagnostic State": "configured_smoke_required", + "Evidence": "Saved session source status records FMP as configured; provider setup still needs a reviewed one-ticker smoke.", + "Next Action": "Run make fmp-smoke TICKER=.", + } +``` + +Use `fmp_stage` for the four varying values in the existing FMP stage row. Keep its completion gate and provider-setup boundary unchanged. + +- [ ] **Step 4: Run focused GREEN and affected project-status tests** + +Run: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q \ + tests/test_project_status.py::test_project_status_fmp_stage_fails_closed_without_recorded_provider_state \ + tests/test_project_status.py::test_project_status_fmp_stage_preserves_explicit_saved_provider_states \ + tests/test_project_status.py::test_project_status_stage_map_classifies_remaining_public_items \ + tests/test_project_status.py::test_project_status_cli_check_uses_fast_generated_artifacts \ + tests/test_next_stage.py +``` + +Expected: all selected tests pass. + +- [ ] **Step 5: Verify current command truth without writing artifacts** + +Run: + +```bash +make project-status-check > /tmp/stock-product-polish-project-status.log +make next-stage > /tmp/stock-product-polish-next-stage.log +rg -n "FMP provider activation|FMP_API_KEY|Provider key status|source status" \ + /tmp/stock-product-polish-project-status.log \ + /tmp/stock-product-polish-next-stage.log +``` + +Expected: project status does not claim FMP appears configured when saved source status is unavailable; next-stage retains its current local provider classification. + +--- + +### Task 4: Affected verification and before/after evidence + +**Files:** +- Verify: `src/dashboard_visual_system.py` +- Verify: `src/dashboard.py` +- Verify: `src/project_status.py` +- Verify: `tests/test_dashboard_visual_system.py` +- Verify: `tests/test_research_workspace.py` +- Verify: `tests/test_dashboard_helpers.py` +- Verify: `tests/test_project_status.py` +- Evidence only: `/tmp/stock-research-product-polish-*` + +**Interfaces:** +- Consumes: all three GREEN tasks. +- Produces: focused test evidence, route screenshots, DOM geometry, and an independent review verdict; no repository artifact. + +- [ ] **Step 1: Run the complete affected test set** + +Run: + +```bash +PYTHONDONTWRITEBYTECODE=1 python3 -m pytest -q -p no:cacheprovider \ + tests/test_dashboard_visual_system.py \ + tests/test_research_workspace.py \ + tests/test_dashboard_helpers.py \ + tests/test_project_status.py \ + tests/test_next_stage.py +``` + +Expected: all tests pass; only previously documented third-party warnings are acceptable. + +- [ ] **Step 2: Run the smallest affected browser matrix** + +Run: + +```bash +POLISH_BROWSER_DIR="$(mktemp -d /tmp/stock-research-product-polish-browser.XXXXXX)" +make workspace-visual-browser-check \ + ROUTES=research-desk,public-home \ + VIEWPORTS=1280x720,390x844 \ + ZOOMS=1,2 \ + OUTPUT_DIR="$POLISH_BROWSER_DIR" +``` + +Expected: 8/8 cells pass with zero runtime, overflow, focus, hierarchy, or external-network failures. + +- [ ] **Step 3: Measure the corrected live layout** + +Using the same local browser session, record the Research Desk desktop +`.sr-evidence-row p` width and height, row height, full text, and horizontal +bounds. Acceptance: + +- reason width is at least 240 CSS pixels at `1280x720` and 100% zoom; +- row height is below 300 CSS pixels; +- both freshness and reason text are complete; +- no horizontal overflow; and +- phone layout is one column with no clipping. + +Save the measurement JSON under `/tmp/stock-research-product-polish-layout.json`. + +- [ ] **Step 4: Build and inspect before/after comparisons** + +Use the accepted prior audit screenshots: + +- `/tmp/stock-research-product-audit-2026-08-14/01-research-desk-desktop.png` +- `/tmp/stock-research-product-audit-2026-08-14/08-research-desk-fullpage-diagnostic.png` +- `/tmp/stock-research-product-audit-2026-08-14/09-public-home-desktop.png` +- `/tmp/stock-research-product-audit-2026-08-14/06-research-desk-phone.png` + +Pair each with its same-viewport current screenshot in a single side-by-side comparison image under `/tmp/stock-research-product-polish-comparison/`. Inspect each combined image and reject any loading, crop, typography, spacing, border, radius, focus, or copy regression. + +- [ ] **Step 5: Run final hygiene and independent review** + +Run: + +```bash +git diff --check +git status --short +git diff --name-only HEAD +``` + +Confirm no `data/` or `outputs/` path changed. Ask an independent reviewer for Critical/Important findings against the approved spec and final evidence. + +- [ ] **Step 6: Commit the verified named files only** + +Only after independent READY: + +```bash +git add -- \ + src/dashboard_visual_system.py \ + src/dashboard.py \ + src/project_status.py \ + tests/test_dashboard_visual_system.py \ + tests/test_research_workspace.py \ + tests/test_dashboard_helpers.py \ + tests/test_project_status.py \ + docs/superpowers/plans/2026-08-14-product-polish-truth-fixes.md +git diff --cached --check +git commit -m "Polish research evidence and status truth" +``` + +Do not push the commit. From 24808ebcc8a57e8efe53179f69c5fd4718bdc13e Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:29:55 -0400 Subject: [PATCH 09/13] Fix Research Desk evidence layout --- src/dashboard_visual_system.py | 18 ++++++++++++++++++ tests/test_dashboard_visual_system.py | 19 +++++++++++++++++++ tests/test_research_workspace.py | 2 ++ 3 files changed, 39 insertions(+) diff --git a/src/dashboard_visual_system.py b/src/dashboard_visual_system.py index d0f75fa9..33fcbca9 100644 --- a/src/dashboard_visual_system.py +++ b/src/dashboard_visual_system.py @@ -706,6 +706,17 @@ def dashboard_visual_system_css() -> str: .sr-evidence-lane {{ display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }} .sr-evidence-row p {{ margin: 0; color: var(--sr-text); overflow-wrap: anywhere; font-size: .8125rem; line-height: 1.5; }} .sr-evidence-count {{ color: var(--sr-muted); font: .8125rem ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }} +.research-desk-brief .sr-evidence-row {{ + grid-template-columns: minmax(12rem, .75fr) minmax(0, 2fr); + align-items: start; +}} +.research-desk-brief .sr-evidence-lane {{ grid-row: 1 / span 2; }} +.research-desk-brief .sr-evidence-count {{ + grid-column: 2; + overflow-wrap: anywhere; + white-space: normal; +}} +.research-desk-brief .sr-evidence-row p {{ grid-column: 2; }} .sr-status-chip {{ display: inline-flex; align-items: center; @@ -926,6 +937,13 @@ def dashboard_visual_system_css() -> str: .sr-answer-panel h2 {{ font-size: 1.0625rem; }} .sr-primary-action, .public-primary-action {{ width: 100%; }} .sr-evidence-row, .sr-timeline-record {{ grid-template-columns: 1fr; }} + .research-desk-brief .sr-evidence-row {{ grid-template-columns: 1fr; }} + .research-desk-brief .sr-evidence-lane, + .research-desk-brief .sr-evidence-count, + .research-desk-brief .sr-evidence-row p {{ + grid-column: 1; + grid-row: auto; + }} }} @media (max-width: 360px) {{ .research-discover-browser-jump {{ diff --git a/tests/test_dashboard_visual_system.py b/tests/test_dashboard_visual_system.py index c3bd5418..cfc5ddeb 100644 --- a/tests/test_dashboard_visual_system.py +++ b/tests/test_dashboard_visual_system.py @@ -166,6 +166,25 @@ def test_dashboard_visual_css_uses_local_fonts_tokens_and_responsive_complete_co assert "@media (max-width: 360px)" in css +def test_research_desk_evidence_layout_reserves_reason_width_and_resets_on_phone(): + css = visual.dashboard_visual_system_css() + desktop = css[ + css.index(".research-desk-brief .sr-evidence-row {") : + css.index(".sr-status-chip {") + ] + mobile = css[css.index("@media (max-width: 640px)") :] + + assert "grid-template-columns: minmax(12rem, .75fr) minmax(0, 2fr)" in desktop + assert ".research-desk-brief .sr-evidence-count" in desktop + assert "overflow-wrap: anywhere" in desktop + assert ".research-desk-brief .sr-evidence-row p" in desktop + assert "grid-column: 2" in desktop + assert ".research-desk-brief .sr-evidence-row" in mobile + assert "grid-template-columns: 1fr" in mobile + assert "grid-column: 1" in mobile + assert "grid-row: auto" in mobile + + def test_typed_components_escape_every_text_and_attribute_boundary(): action = visual.SafeRouteAction( label="Open ", diff --git a/tests/test_research_workspace.py b/tests/test_research_workspace.py index 92fc21aa..1b40b333 100644 --- a/tests/test_research_workspace.py +++ b/tests/test_research_workspace.py @@ -1568,6 +1568,8 @@ def test_research_desk_brief_and_advanced_evidence_html_stay_answer_first_and_co "data-sr-region='supporting-evidence'" ) assert "What needs my attention today?" in desk_html + assert "Saved readiness is current." in desk_html + assert "No unresolved saved source-change item is available." in desk_html assert "Freshness" in desk_html assert "Current for saved sources" in desk_html assert ">current<" not in desk_html.casefold() From cbac45c496167fd30aa1360d3b49d28415b3e4b5 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:32:38 -0400 Subject: [PATCH 10/13] polish public home peer metric wording --- src/dashboard.py | 2 +- tests/test_dashboard_helpers.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dashboard.py b/src/dashboard.py index 44ac4924..d76aba6a 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -6676,7 +6676,7 @@ def public_home_overview_html(summary: dict[str, object]) -> str: f"
Tracked names
{master:,}
" f"
Price-ready
{price_ready:,}
" f"
DCF-ready
{dcf_ready:,}
" - f"
Trusted peers
{peer_ready:,}
" + f"
Mapped peer trend
{peer_ready:,}
" "" "" f"{advanced_detail_marker_html().value}" diff --git a/tests/test_dashboard_helpers.py b/tests/test_dashboard_helpers.py index 06677b14..feaa45dd 100644 --- a/tests/test_dashboard_helpers.py +++ b/tests/test_dashboard_helpers.py @@ -31285,6 +31285,8 @@ def test_public_home_overview_keeps_one_start_action_and_compact_readiness_snaps assert "3,540" in html assert "2,693" in html assert "29" in html + assert "
Mapped peer trend
29
" in html + assert "Trusted peers" not in html assert "No data, no conclusion" in html From 79f17a91bf28aedc2cf5f410301d123c14e29a45 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:37:57 -0400 Subject: [PATCH 11/13] Fail closed on missing FMP provider status --- src/project_status.py | 41 +++++++++++++++-------- tests/test_project_status.py | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/src/project_status.py b/src/project_status.py index 7fc42577..9256cb8a 100644 --- a/src/project_status.py +++ b/src/project_status.py @@ -616,9 +616,11 @@ def _remaining_public_stage_rows( ) -> list[dict[str, str]]: """Classify the remaining public/product stages without unlocking data.""" source_operator_summary = source_operator_summary if isinstance(source_operator_summary, dict) else {} + raw_needs_setup = source_operator_summary.get("needs_setup") + provider_status_recorded = isinstance(raw_needs_setup, list) needs_setup = [ str(item).strip().lower() - for item in source_operator_summary.get("needs_setup", []) + for item in (raw_needs_setup if provider_status_recorded else []) if str(item).strip() ] avoid_repeating = [ @@ -639,6 +641,27 @@ def _remaining_public_stage_rows( fmp_missing = "fmp" in needs_setup first_provider = first_setup.get("setup_env") or "FMP_API_KEY" + if not provider_status_recorded: + fmp_stage = { + "State": "source_status_review_required", + "Diagnostic State": "source_status_unavailable", + "Evidence": "FMP configuration is not established from saved session status.", + "Next Action": "Run make provider-setup-checklist to inspect current local setup.", + } + elif fmp_missing: + fmp_stage = { + "State": "awaiting_external_setup", + "Diagnostic State": "external_key_required", + "Evidence": "FMP_API_KEY is not configured in the saved session source status.", + "Next Action": "Set FMP_API_KEY outside the repo, then run one reviewed ticker smoke.", + } + else: + fmp_stage = { + "State": "configured_smoke_required", + "Diagnostic State": "configured_smoke_required", + "Evidence": "Saved session source status records FMP as configured; provider setup still needs a reviewed one-ticker smoke.", + "Next Action": "Run make fmp-smoke TICKER=.", + } source_queues_exhausted = trusted_data_pilot_has_candidates is False and price_coverage_complete avoid_source_ladder = "fundamentals_share_count_source_ladder" in avoid_repeating linkedin_stage = _linkedin_stage_from_git_status(git_status_line) @@ -684,18 +707,10 @@ def _remaining_public_stage_rows( }, { "Stage": "FMP provider activation", - "State": "awaiting_external_setup" if fmp_missing else "configured_smoke_required", - "Diagnostic State": "external_key_required" if fmp_missing else "configured_smoke_required", - "Evidence": ( - "FMP_API_KEY is not configured." - if fmp_missing - else "FMP_API_KEY appears configured; provider setup still needs a reviewed one-ticker smoke." - ), - "Next Action": ( - "Set FMP_API_KEY outside the repo, then run one reviewed ticker smoke." - if fmp_missing - else "Run make fmp-smoke TICKER=." - ), + "State": fmp_stage["State"], + "Diagnostic State": fmp_stage["Diagnostic State"], + "Evidence": fmp_stage["Evidence"], + "Next Action": fmp_stage["Next Action"], "Completion Gate": ( f"{first_provider} is configured locally; one ticker validates, previews narrowly, has zero rejected rows, and source provenance is present." ), diff --git a/tests/test_project_status.py b/tests/test_project_status.py index e34b5504..b195cae1 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -1169,6 +1169,69 @@ def test_project_status_human_output_uses_workflow_evidence_when_proof_queues_ar assert "avoid repeating now: fundamentals_share_count_source_ladder" not in output +@pytest.mark.parametrize( + "source_operator_summary", + (None, {}, {"needs_setup": "fmp"}), +) +def test_project_status_fmp_stage_fails_closed_without_recorded_provider_state( + source_operator_summary, +): + rows = project_status._remaining_public_stage_rows( + { + "tickers_total": 10, + "tickers_with_prices": 2, + "tickers_usable_for_momentum": 2, + "tickers_fundamentals_ready": 1, + "tickers_dcf_ready": 1, + "tickers_peer_ready": 0, + "data_gaps": 8, + "data_sources_optional_locked": 3, + }, + source_operator_summary=source_operator_summary, + git_status_line="## main...origin/main", + ) + stage = next(row for row in rows if row["Stage"] == "FMP provider activation") + + assert stage["State"] == "source_status_review_required" + assert stage["Diagnostic State"] == "source_status_unavailable" + assert "not established from saved session status" in stage["Evidence"] + assert stage["Next Action"] == "Run make provider-setup-checklist to inspect current local setup." + assert "appears configured" not in " ".join(stage.values()) + + +@pytest.mark.parametrize( + ("needs_setup", "expected_state", "expected_diagnostic"), + ( + (["fmp", "alpha_vantage", "finnhub"], "awaiting_external_setup", "external_key_required"), + (["alpha_vantage", "finnhub"], "configured_smoke_required", "configured_smoke_required"), + ([], "configured_smoke_required", "configured_smoke_required"), + ), +) +def test_project_status_fmp_stage_preserves_explicit_saved_provider_states( + needs_setup, + expected_state, + expected_diagnostic, +): + rows = project_status._remaining_public_stage_rows( + { + "tickers_total": 10, + "tickers_with_prices": 2, + "tickers_usable_for_momentum": 2, + "tickers_fundamentals_ready": 1, + "tickers_dcf_ready": 1, + "tickers_peer_ready": 0, + "data_gaps": 8, + "data_sources_optional_locked": 3, + }, + source_operator_summary={"needs_setup": needs_setup}, + git_status_line="## main...origin/main", + ) + stage = next(row for row in rows if row["Stage"] == "FMP provider activation") + + assert stage["State"] == expected_state + assert stage["Diagnostic State"] == expected_diagnostic + + def test_project_status_stage_map_classifies_remaining_public_items( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, From 674e57514e005d30c6ee4d8b62d0a5bfd1e0847b Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 20:59:49 -0400 Subject: [PATCH 12/13] Handle malformed FMP saved status --- src/project_status.py | 2 +- tests/test_project_status.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/project_status.py b/src/project_status.py index 9256cb8a..ff342a0a 100644 --- a/src/project_status.py +++ b/src/project_status.py @@ -628,7 +628,7 @@ def _remaining_public_stage_rows( for item in source_operator_summary.get("avoid_repeating", []) if str(item).strip() ] - first_setup = _source_operator_first_setup_guidance(source_operator_summary) + first_setup = _source_operator_first_setup_guidance({"needs_setup": needs_setup}) total = int(summary.get("tickers_total") or 0) with_prices = int(summary.get("tickers_with_prices") or 0) price_ready = int(summary.get("tickers_price_ready") or with_prices) diff --git a/tests/test_project_status.py b/tests/test_project_status.py index b195cae1..762f2b22 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -1171,7 +1171,14 @@ def test_project_status_human_output_uses_workflow_evidence_when_proof_queues_ar @pytest.mark.parametrize( "source_operator_summary", - (None, {}, {"needs_setup": "fmp"}), + ( + None, + {}, + {"needs_setup": "fmp"}, + {"needs_setup": None}, + {"needs_setup": 0}, + {"needs_setup": False}, + ), ) def test_project_status_fmp_stage_fails_closed_without_recorded_provider_state( source_operator_summary, From d5b6d1a4c61d9639d64dc131cae297064140d777 Mon Sep 17 00:00:00 2001 From: davidjiang8888 Date: Fri, 14 Aug 2026 21:04:58 -0400 Subject: [PATCH 13/13] Render malformed FMP status safely --- src/project_status.py | 7 +++--- tests/test_project_status.py | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/project_status.py b/src/project_status.py index ff342a0a..f6cddd63 100644 --- a/src/project_status.py +++ b/src/project_status.py @@ -2040,9 +2040,10 @@ def _print_human( print(f"- Data sources: {summary['data_sources_available']}/{summary['data_sources_total']} available") print(f"- Required data sources needing attention: {summary['data_sources_needing_attention']}") source_operator_summary = payload.get("source_operator_summary", {}) + raw_needs_setup = source_operator_summary.get("needs_setup") if isinstance(source_operator_summary, dict) else None source_needs_setup = ( - [str(item).strip() for item in source_operator_summary.get("needs_setup", []) if str(item).strip()] - if isinstance(source_operator_summary, dict) + [str(item).strip() for item in raw_needs_setup if str(item).strip()] + if isinstance(raw_needs_setup, list) else [] ) if source_needs_setup: @@ -2132,7 +2133,7 @@ def _print_human( free_tier_limits = _source_operator_free_tier_limit_summary(source_operator_summary) if free_tier_limits: print(f"- Free-tier limits: {free_tier_limits}.") - first_setup = _source_operator_first_setup_guidance(source_operator_summary) + first_setup = _source_operator_first_setup_guidance({"needs_setup": source_needs_setup}) if first_setup: print(f"- Configure first provider: {first_setup['setup_env']}.") print(f"- Reviewed one-ticker smoke after setup: {first_setup['smoke_command']}.") diff --git a/tests/test_project_status.py b/tests/test_project_status.py index 762f2b22..210e1e84 100644 --- a/tests/test_project_status.py +++ b/tests/test_project_status.py @@ -1239,6 +1239,53 @@ def test_project_status_fmp_stage_preserves_explicit_saved_provider_states( assert stage["Diagnostic State"] == expected_diagnostic +@pytest.mark.parametrize("needs_setup", (None, 0, False)) +def test_project_status_human_output_fails_closed_on_malformed_saved_provider_state( + needs_setup, + capsys: pytest.CaptureFixture[str], +): + payload = { + "summary": { + "data_sources_available": 0, + "data_sources_total": 0, + "data_sources_needing_attention": 0, + "data_sources_optional_locked": 0, + "data_gaps": 0, + "tickers_with_prices": 0, + "tickers_total": 0, + "tickers_usable_for_momentum": 0, + "tickers_fundamentals_ready": 0, + "tickers_dcf_ready": 0, + "tickers_peer_ready": 0, + "onboarding_actions": 0, + "critical_actions": 0, + "purpose_evaluation_groups": 0, + "purpose_evaluation_active_groups": 0, + }, + "warnings": [], + "source_operator_summary": {"needs_setup": needs_setup}, + "remaining_public_stage_rows": [ + { + "Stage": "FMP provider activation", + "State": "source_status_review_required", + "Diagnostic State": "source_status_unavailable", + "Evidence": "FMP configuration is not established from saved session status.", + "Next Action": "Run make provider-setup-checklist to inspect current local setup.", + } + ], + "workflow_continuation": {}, + "recommended_next_command_rows": [], + "top_onboarding_actions": [], + } + + project_status._print_human(payload) + output = capsys.readouterr().out.lower() + + assert "optional provider setup gaps:" not in output + assert "source setup to unlock more:" not in output + assert "fmp provider activation: source_status_review_required" in output + + def test_project_status_stage_map_classifies_remaining_public_items( tmp_path: Path, monkeypatch: pytest.MonkeyPatch,