Updates
What changed in the application recently, what each change was for, and what has explicitly not been done yet.
Rebranded to IndicaView, and repositioned as a hosted service
The product was renamed to IndicaView and repositioned. It is a hosted SaaS web application: nothing to install, nothing to self-host.
What changed in the product and in these docs:
| Before | Now | |
|---|---|---|
| Name | the previous working name | IndicaView |
| Delivery | “runs on your machine”, “local-first” | hosted — the browser application is served as static files |
| Cost | “free to use” | usage metered in credits — no plan, price or credit cost decided yet |
| Accounts | optional, manages a profile | optional; adds a profile and a security tab, and is where a credit balance and billing details will live once metering is switched on |
| Source repository | advertised in the landing page’s SoftwareSourceCode schema | removed — a commercial hosted product does not advertise a source repo |
Nothing about billing, checkout, plans or credit consumption is implemented. What
exists is the seam: a canonical product/commercial configuration
(app/js/brand/product-config.js), a credit-balance accessor
(app/js/commercial/credits.js) that reports “unavailable” rather than
inventing a number, and reserved (nullable, unpopulated) credit columns on the
account profile. See Commercial model — or, in the
repository, docs/commercial-model.md.
The retired name and the retired positioning have been removed from every
user-facing surface, and scripts/check-product-config.cjs fails the build if
either comes back.
Four substantial features
The largest changes since this documentation was last written. Each has its own page.
| Feature | Page | One line |
|---|---|---|
| Supabase sign-in and My Account | Authentication | Optional; guests keep the entire workspace |
| NSGA-II | Algorithms | A seventh, multi-objective optimizer |
| Shareable result card | Sharing & export | A 2160 × 3840 PNG rendered client-side |
| Responsive / mobile layout | Mobile | A phone layout below 768 px |
Authentication
Optional Supabase sign-in integrated directly into the static workspace, plus a My Account area with Profile and Security tabs. Email and password only; no OAuth, no MFA, no account deletion.
The important design point is that a single constant decides whether sign-in is required, and
the default is 'optional' — guests reach the chart, the optimizer and the analysis without an
account, and a missing or unreachable auth backend degrades to that state rather than breaking.
Profile data is protected by row-level security rather than by the routing flag. Details, the RLS model and the unverified email-delivery path are in Authentication.
NSGA-II
A seventh optimizer, for when two objectives genuinely conflict — profit against drawdown, say. Trials become vectors rather than scalars, and the population is sorted into Pareto fronts.
It ships alongside GA rather than replacing it, and shares GA’s operators and non-dominated sort. A follow-up commit closed four correctness and honesty defects, and forced the legacy contract back on the new code so the two algorithms stay comparable.
Result-card export
A client-rendered PNG summarising an optimizer run — drawn with canvas, at 2160 × 3840, with no network request. Your Pine source and the winning parameter values are excluded by a whitelist rather than a blacklist.
It is reachable from the optimizer results, the Analysis toolbar and the detached window — though on desktop the results control now lives in a kebab rather than on the toolbar. See Sharing & export.
Responsive layout
A phone layout below 768 px, built as a phone-first enhancement rather than a desktop reduction: a two-tab pane switcher, bottom sheets replacing selects and dropdowns, kebab menus replacing button groups, measured column pins, and 44 px touch targets achieved by growing hit areas rather than painted boxes.
Most of the recorded before/after numbers exist because the first attempt looked plausible in code and was visibly wrong on a device. See Mobile.
Supporting changes
- Visual language — drawdown redrawn throughout, the Run control reduced to a play glyph and one word, and an Apple-style sign-in treatment.
- New in-repo docs —
docs/authentication.md,docs/SHARE_CARD.mdanddocs/mobile-ux-audit.md. All three are more detailed than this site; two of them are themselves slightly stale, and this site records where. - A smoke aggregate runner —
scripts/run-smokes.cjs, which times out per file and recordsTIMEOUTrather than skipping quietly. - Auth test suites — a unit tier and a Playwright tier, both against mocked Supabase HTTP.
- RLS migrations under
supabase/migrations/, with rollback-only SQL checks undertests/auth/.
Where the documentation itself disagrees
Worth knowing before you trust any internal design note:
- Three files under
docs/are ported upstream reports from a different host toolchain. They describe anastra.cmdharness and a chart backend this repository no longer uses. Treat them as provenance, not as current state. - The repository’s own
README.mdanddocs/architecture.mddescribe an earlier phase of the application. Where they conflict with this site or with the code, the code is right. - One consequence you can verify yourself: the charting library was swapped from ApexCharts to
Apache ECharts 6.1.0 in
790cfbb, because ApexCharts requires an OEM licence once embedded in a third-party product. Any note still describing ApexCharts is stale. docs/SHARE_CARD.mdis close but has drifted in three places: it says the result card hands the file to the OS share sheet on a phone (that path is unreachable), it shows the desktop control as a visible button (it is now in a kebab), and it describes the footer as configuration-driven (that one string is hardcoded).
Recent changes
Grouped by theme rather than by date.
Optimizer ownership and data contracts
The optimizer was progressively moved from a detached results window onto an in-workspace surface, while the canonical data layer was kept intact.
| Change | What it did | Commit |
|---|---|---|
| Ownership handover | Moved 45 canonical-only controls into the new workspace shell. Deliberately kept two canonical capabilities the upstream shell lacked: the algorithm selector (upstream ships 5 methods, canonical ships 7) and the absolute maxDrawdown sort option | 3e179d4 |
| Ownership regression guard | Added a browser-free static check that parses the page and asserts no duplicate ids and exactly one owner per opt-* id. It also corrected an inventory error in the process — an id that had been counted as canonical-only is in fact shared | 0130ad0 |
| Load Params + snapshot producer | Added applyValues() and a snapshot event, writing through the editor’s own controls so the change listener rebuilds the override map. Refuses while a run is active | 757bf95 |
| Results table presentation | Swapped in the upstream table while keeping canonical’s data layer. Trial identity is preserved: sorting and filtering return the same trial objects wrapped with their original index, never cloned or renumbered | 559af7e |
| Analysis workspace activated | Brought the analysis surface up on canonical snapshots, merging back five canonical contracts including honest total/truncated trial reporting and detail-on-demand | ce260f2 |
| Live results and run controls | Connected real runs to the UI. Verified with a real TPE run reaching finished with the correct row count, plus real SA and GA runs through the same boundary | 41a6651 |
The pattern behind these commits
The recurring discipline is that the two halves are merged, not that one side is rebuilt around the other. The upstream presentation was adopted; canonical behaviours were merged into it. Every externally consumed API survived the swap.
That is why a change like “bring in the upstream table” is a large-looking commit that is actually low-risk: the data contract is unchanged, and there is a static guard asserting that the DOM has exactly one owner per id.
Correctness fixes
| Change | What it did | Commit |
|---|---|---|
| Trial identity preserved | Flipped trial identity from array position to canonical run index, so selection survives sorting and filtering | 270f718 |
| Chart load order + chart dependencies | Moved the chart library <script> into <head> ahead of the editor’s loader, and restored a status-marker structure that the ownership handover had flattened | 399745a |
| Series styling merge | A series override was replacing the whole series array and silently dropping 40 drawdown points. Styling now merges by index | ab6bb01 |
| Stepped progression | A step set on the wrong object was a no-op, so the best-so-far curve rendered as a plain line. Found by pixel-diffing a rendered canvas, not by inspecting the option | fafdd60 |
| Canvas-safe labels | Heatmap cell labels were emitted as HTML, which a canvas renderer does not parse. A formatter left bound to a categorical axis rendered every year as a dash | 0df5d39 |
| Navigation and responsive shell | Market controls pushed the run button off-screen at narrow widths; controls are now wrapped in a scroller, and a snapshot fast path left a view blank when toggled repeatedly | 2da0bd5 |
The transferable lesson from the chart fixes
Three of these chart bugs produced no error. The chart rendered, it just rendered wrongly — a missing step, missing points, wrong labels.
They were caught by asserting on rendered output (pixel counts from a real canvas), not by asserting that the option object was well-formed. The option object was well-formed in every one of these cases; it was being applied to the wrong place.
Tooling and documentation
| Change | What it did | Commit |
|---|---|---|
| Support files ported unwired | Presentation stylesheets, browser smoke suites and UI baselines landed ahead of the wiring that would use them | 589de7d, 73528d8, 686e603, 65b3a88 |
| Documentation site | This site | 892a179 |
Phase F
Phase F is the most recent delivery sequence, and it is unusually well documented because each commit carries a detailed body.
| Step | What it delivered |
|---|---|
| F1 | Mounted and drove the backtest presentation. It was inert by construction, not merely unwired: the five chart host elements did not exist, and the height function threw before its own null guard, aborting the rest of the animation frame |
| F2 | Connected the in-page analysis detail request path. The analysis surface could already ask for a trial’s detail; the coordinator that computes it existed; nothing in between did, so every in-page request failed |
| F3 | Made the chart follow the application theme. It had been pinned to dark at construction, so light mode left a dark chart in a light workspace |
| F4 | Added a light editor theme and made the editor follow the application theme, carrying the same token rules with light-appropriate values rather than inventing a second palette |
| F5 | Removed superseded wiring — mostly a list of what it refused to remove, and why |
| F6 | Added the browser smoke for the whole sequence |
F1's ownership rule, and why it was the whole point
The five chart hosts are created as siblings of the tab panels that the application
rewrites via innerHTML on every run.
A host placed inside a panel would be destroyed the next time a backtest ran. That single decision is why the application code never has to write presentation markup, and it is the reason F1 was safe to land as a staged change.
F5's refusals are the interesting part
F5 removed one unreachable click binding. The commit body is mostly a list of what was deliberately retained, each with a reason:
- The detached window’s open function and its call sites — the detached surface is not yet owned by the replacement; retiring it is a decision for review, not for this phase.
- The detached page and its window manager, for the same reason.
- The export bindings — live, and part of a frozen contract.
- Sidebar persistence and the divider-resize handler.
It also records a test bug worth noting: the smoke assertion matched the function definition as well as the call, so it would have failed even after the correct removal. It now matches the binding specifically.
The optimizer redesign
The redesign was a presentation and control-surface change, explicitly not an engine change. Engine algorithms, scoring, metric calculation, search-space rules, Pine sources, market-data behaviour, database names and database version were all left alone — and the engine-versus-synthetic distinction between real and reconstructed results was preserved throughout.
The shipped architecture is a deliberate four-way split:
| Layer | Owner |
|---|---|
| Data and lifecycle | the canonical controller |
| Presentation | the workspace, results table and analysis modules |
| DOM ownership | ~90 ids, one owner each, statically guarded |
| Application bridges | a handful of calls; no presentation markup |
The parallel evaluation decision, which reversed itself
Parallel evaluation was first rejected on measurement — a benchmark showed the pool running at 2–11 % of sequential speed — and then implemented, because the benchmark was broken rather than the system.
The cause: the benchmark passed the wrong field names, so the Pine engine received no source at all and every trial failed in ~0.2 ms without doing work. The benchmark now refuses to report timings when nothing reached the engine.
Corrected numbers, at the application’s default depth: 518 ms per trial, break-even at roughly 13 trials, best observed speedup 3.33×. Details and the invariants that keep batched runs identical to serial ones are in Execution.
The redesign document closes by superseding itself and pointing at the analysis implementation notes, so treat it as a historical record of a decision rather than current behaviour.
What is in flight
Everything here is a real, sourced “not done”.
Explicitly deferred
- Live email delivery is unverified. Signup acceptance, confirmation, reset and a real authenticated browser save/reload have not been confirmed end to end. Dashboard redirects, email templates and SMTP are manual steps no tooling can check.
- The NSGA-II Pareto archive is never consumed. The search maintains it; only the current generation’s front is displayed.
- The result card’s OS share path is unreachable. The code exists and the UI never passes the option it needs, so every path ends at Download PNG. There is also no copy-to-clipboard.
- No mobile smoke test. Every responsive claim rests on manual measurement in Chrome device emulation; safe-area insets, non-Chromium engines and real devices are untested.
- Live market integration and Python hyperopt are deferred. The Python optimiser work was neither run nor repaired as part of this sequence.
- Walk-forward / out-of-sample validation is a later phase. Until it exists, every sweep result is a candidate, not a validated edge. Extending to prop-firm simulation does not fix this — in-sample variance is still in-sample.
- Zero-copy worker transport is designed and disabled; it needs headers a plain static deployment cannot set.
- Multi-asset support is blocked: adding a field to the market descriptor activates a module-level, order-populated cache inside the vendor bundle.
Superseded conclusions
- “Parallel evaluation is not worth it” — retracted. The correct statement is 3.33× at the default depth, gated by depth.
- The detached optimiser-results window — superseded by the in-workspace analysis surface. Legacy links redirect.
- ApexCharts — superseded by ECharts in this repository.
Retained but unowned
The detached window surface and its page are still present and still openable, but are explicitly not yet owned by the replacement. Retiring them is an open decision, not a pending task.
Verification gaps
- The browser smoke skips rather than fails when its automation dependency is missing.
- The end-to-end run-to-charts assertion was explicitly deferred out of the step that would naturally have covered it.
- Five seeded browser trials cover startup sampling — not mature convergence, and not every algorithm or strategy. No large stress run has been performed in the browser.
- Storage-quota exhaustion and delayed-write ordering were tested with controlled adapters, not by exhausting real storage.
- No screen-reader session, no physical touch-device test, no blocked-popup test, and no exhaustive malformed-record test. Narrow-layout checks used desktop viewport emulation.
Two things that are broken, not merely stale
Both are in the test tree and are worth a small fix pass before they get quoted anywhere:
- One smoke file’s header comment still carries the pre-correction id counts, while its own assertions carry the corrected ones. A reader who trusts the comment will state the wrong numbers.
- Another smoke file’s header says a module “is deliberately not migrated yet” — which stopped being true when that migration landed. The block is written to self-disable, so it is inert rather than broken, but the comment is now misleading.
Next
- Architecture decisions — the invariants these changes preserve.
- Limitations — the consolidated list of what the product does not establish.