feat(web): read-only DCA plan card on /rules - #850
Merged
Merged
Conversation
…e, read-only The reader calls build_dca_plan with the admission screen resolved per call; no budget answers an awaiting_budget payload of the same shape, and every DcaPlanError (bad input, or a config refusal such as case-colliding target_weights) is a 400 ApiRefusal carrying the service's own words. The payload carries cap_check: the worst UTC calendar month rail 14 was checked against (#847), worded by a new service function, worst_month_text, which the CLI's 'checked against the cap:' line now also uses -- one sentence for both front-ends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ommand The rules view reads /api/dca-plan as its secondary endpoint; its inputs come from the page address (?budget=&buffer=), copied once into that endpoint's query only. The card builds no control: tags on a closed list, one code element filled from plan.command, one unsorted table. It shows the worst-calendar-month figure as 'checked against the cap', and a refused read keeps the ledger standing and places the server's reason. Structural client tests: the card's three collections are enrolled in the row-key scan with a seeded, admitted plan; every payload key and summary figure is paired with a read; the SW pin covers every API route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eaitbrahim
commented
Sep 28, 2026
This was referenced Sep 28, 2026
…ip bonus (#851, #852) - The awaiting template carries --config/--db like the filled command (R17, #851). - A degraded rail 14 cap says why, as the CLI prints it: "$0.00 (because ...)" (#852). - An existing rule's row carries the CLI's (dip_bonus_pct X) marker. - parse_plan_inputs refuses more than 12 decimal places, so a 25-character 1e-10000000 can no longer be spelled out as megabytes of JSON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eaitbrahim
commented
Sep 28, 2026
eaitbrahim
left a comment
Contributor
Author
There was a problem hiding this comment.
Round 2: all three round-1 threads verified on e46ee5e and resolved (#851, #852 closed). The 12-decimal-place bound refuses 1e-10000000 for budget and buffer, and 0e10000000 formats as 0. No new findings. Tests (tests/web and test_dca_plan.py, 1136) pass and node --check is clean on render.js and main.js.
eaitbrahim
added a commit
that referenced
this pull request
Sep 28, 2026
…n and its read-only card (#877) MINOR: a rail changes scope for DCA (#869) and new commands and routes land (#846, #850). No schema change since 0.19.0. What lands: #846 (#831 follow-up) -- keel dca plan: schedule by target weights within rail 14's worst calendar month; [Y] writes candidate rules only. #850 -- GET /api/dca-plan and a read-only card on /rules. #869 (#853) -- rail 6 (per-asset) no longer vetoes DCA buys; the plan blocks on rail 3 and warns on rail 5. #867 (#854), #876 (#874) -- a blocked plan names the largest passing budget and smallest passing buffer, clearing rails 14, 3 and 2. #870 (#856) -- admission screen cached 5 minutes. #858 (#855) -- deploy/live-rules.json synced. #859 (#857) -- design spec for the DCA sleeve's sell side. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 2 of
docs/superpowers/plans/2026-09-27-dca-plan.md(Tasks 7–8). This PR builds on PR 1 (#846) as merged, not on the plan's snippets.What it does
GET /api/dca-plan?budget=&buffer=answers from the same service askeel dca plan(build_dca_plan). The admission screen is resolved per call.budget: the route answers anawaiting_budgetpayload with the same keys as the ready one, every figure absent, and the command template.cap_checkis the worst UTC calendar month rail 14 was compared against, with the checked total as itsvalue.badover the cap,goodwithin it, andneutralwhen the tier is unlimited./rulessits under the rule ledger and reads the page's own address:/rules?budget=500&buffer=0.1.--config/--dbfor the served deployment.?budget=, or a config the service refuses) leaves the ledger standing. The card says it could not read the proposal and places the server's reason, in the service's own words.Rulings
DcaPlanErroris a 400ApiRefusal. This includes the onesbuild_dca_planraises over the config (target_weightskeys colliding by case, a non-finite weight).ApiRefusalis this module's only way of saying "declined, and here is why". The CLI refuses the same error as a clean message. A 500 would read "That report could not be built" over a message that already names the fix.dca_plan.worst_month_text(plan). The CLI'schecked against the cap:line and the payload'scap_check.displayboth use it.erroras well as itsdata(rulesView(data, sort, onSort, plan, planError)).dataalone, a 400 would show a bare "could not be read" and throw away the reason, such as the fraction hint forbuffer=10.keel-shell-<full_version>, which is<version>+<commit>, so every new commit is a new cache./api/is never cached, and a new test pins that everyAPI_ROUTESpath sits underAPI_PREFIX.buys,excluded,existing) are enrolled in the row-key scan, not exempted. A seeder patches the screen to admit BTC and reject PAXG, and writes an existing ETH DCA rule. The endpoint is asked with a budget, so every collection has a row._seed_fornow takesmonkeypatch.Test evidence
uv run pytest -q: 7021 passed, 3 skipped (exit 0).uv run ruff check keel tests: exit 0.uv run ruff format --check keel tests: exit 0.uv run mypy(bare): exit 0.node --checkonrender.jsandmain.js: exit 0 for both.button.codeelement, paired withplan.command.plan.cap_check, under "checked against the cap".ROUTESrow, and themountcall passing both the reading'sdataand itserror.location.searchread, namesbudget/bufferonly, and thedca-planbag only.screen_productimport fails the route tests (the patch no longer reaches the reader).DcaPlanErrorcatch fails the config-refusal test.row.fee,item.reasons,rule.per_buy) fails the row-key scan.cap_checkfigure, dropping a table cell, droppingplanReading.error, widening the seeded names, and not placingerror.detaileach fail their test.keel serveon a copy ofkeel/templates/config.yamlwith a temp DB, on a free port. The service worker was unregistered and its caches deleted first./rules?budget=500&buffer=0.1shows the blocked state with the real screen: nothing admitted on an empty DB, every asset listed as excluded with the screen's reasons, and the exact command./ruleswith no query shows the awaiting state and the command template.codeelement. The rule ledger stays in place.buffer=10shows the 400 detail ("…0.1 means hold back 10%") under the standing ledger. The only console entry is the browser's network log of that 400.Deviations from the plan
cap_check, the dca plan: cap check uses an average month; rail 14 caps the calendar month, so 5-buy months veto #847 amendment.rulesViewtakes a fifth parameter,planError(R23).excludedandexistinglists are built with.mapover explicit keys, so the row-key scan checks them. The plan had one generic list helper that read every key..dca-plan .dca-commandCSS rule shows the command as a selectable block.🤖 Generated with Claude Code