Silent unrenderable discriminators, a missing question-contract golden, and a always-on tool table were hiding fail-closed drops behind the prompt wall.
Co-authored-by: Cursor <cursoragent@cursor.com>
Closing a discriminator used to leave GET without a card after refresh.
Write the next dated question in the same request, skip childhood career
and move probes, and do not continue a read-only turn when that question
is already persisted.
Co-authored-by: Cursor <cursoragent@cursor.com>
Empty snapshot candidates were starving remaining D24 splits, so the
TypeScript follow-up chain asked the low-gain Python career probe.
Read paths now share one inference+engine catalog and yield a stale
low-gain distinguish card to the current winner.
Co-authored-by: Cursor <cursoragent@cursor.com>
Python and TypeScript now share a four-option probe contract, persist Focus before asking, and pick the highest-value renderable probe instead of preferring low-gain career events over D24.
Co-authored-by: Cursor <cursoragent@cursor.com>
Staging web never became healthy because /api/health selected migration.schema_migrations as app_runtime, which is forbidden, so Docker rolled the image back.
Co-authored-by: Cursor <cursoragent@cursor.com>
Three collected events with a reserved holdout were stalling because the discriminator door counted holdout. Public selection_allowed still had snapshot fallbacks, and health only proved the image SHA.
Co-authored-by: Cursor <cursoragent@cursor.com>
Showing a choice card is no longer treated as completion. Distinguish probes
require real candidate groups, holdout stays out of scoring, and ordinary
sessions can finish with a credible range instead of an exact-minute gate.
Co-authored-by: Cursor <cursoragent@cursor.com>
Recorded-year quality probes were spoken-only, so the interview had no
choice card. Compare also re-scored after batch until the 105s attempt
aborted the turn.
Co-authored-by: Cursor <cursoragent@cursor.com>
BUG-368 dropped createServerSupabaseClient while keeping the call, so staging publish failed at next build after validate skipped the production build.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mastra intermediate text-delta was published as answer.delta, then set-focus domain errors reset the attempt and replayed evidence. Publish only the terminal no-tool step, persist the next probe on the server, and ground batch quotes in the source turn.
Co-authored-by: Cursor <cursoragent@cursor.com>
Clicking A/B/C/D or stop must persist the answer, close the probe, and
update posteriors in one idempotent transaction instead of sending the
option text as a chat message.
Co-authored-by: Cursor <cursoragent@cursor.com>
Coverage complete only unlocks discrimination. A 34/33/33 window plus an
occupation note must ask a D9/D10 contrast probe instead of offering a
stale winner card.
Co-authored-by: Cursor <cursoragent@cursor.com>
Choice C/D without new evidence never changed the candidate posterior until the next dated-event rescore, and persist-v2 would cache-hit on the same evidence fingerprint. Patch the latest decision_receipt.inference_state in place so the next follow-up sees the asked split immediately.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep provider thinking on a separate channel so process talk is not billed as the spoken reply (BUG-359).
Co-authored-by: Cursor <cursoragent@cursor.com>
BUG-353 fallbacks used undefined window columns and un-narrowed clock/focus values, so Docker next build failed typecheck and could not publish the already-pushed staging head.
Co-authored-by: Cursor <cursoragent@cursor.com>
Disable provider thinking so Flash CoT cannot fill max_tokens, raise the
spoken budget to 16384, emit a server-owned step tree, and continue once
when the body ends on length.
Co-authored-by: Cursor <cursoragent@cursor.com>
Users pick a clock range instead of a coarse period plus notes, so
rectification and window consult scan that range instead of a leftover afternoon bucket.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep report_schema_invalid for the user, but record the inner check, retry plan bind once, and format self-hosted timestamptz so the report list no longer shows 时间未知.
Co-authored-by: Cursor <cursoragent@cursor.com>
Thinking disappeared on failure and never reached session storage. Keep the
sanitized chain on disk and on errors, and regroup the sidebar around reports,
charts, favorites, and dated history titles.
Co-authored-by: Cursor <cursoragent@cursor.com>
Conflict nodes stay server-owned; the Agent writes the question and option copy so users can tap instead of typing through an interrogation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Rectification stays optional. Reported minutes can consult and generate reports; date-plus-period uses a declared window instead of a midpoint or 00:00. Updates BUG-341.
Co-authored-by: Cursor <cursoragent@cursor.com>
Rectification dropped tool.activity started events and treated length finishes as completed. Share generation settings with consultation, keep the activity line through streaming, and name multi-domain chart calculation.
Co-authored-by: Cursor <cursoragent@cursor.com>
Transcript limits were collapsing the create schema, and joined profile selects typed as GenericStringError, so Docker next build failed after tests passed.
Co-authored-by: Cursor <cursoragent@cursor.com>
Incomplete Flash generations were billed as completed consultations. Fail
those runs, keep the partial text, and reuse the rectification like/copy/rerun
bar on ordinary chat replies.
Co-authored-by: Cursor <cursoragent@cursor.com>
Stop accepting client-supplied birth data on those paths, and cap session writes plus location lookups so a logged-in caller cannot farm compute.
Co-authored-by: Cursor <cursoragent@cursor.com>
The homepage was waiting on a full chart plus four extra engines, so the card timed out and showed the failure copy on every visit.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep comparative tables in chat, but hide the long audit behind a collapsed control so the reply stays readable.
Co-authored-by: Cursor <cursoragent@cursor.com>
Web answers were thinner than a local Agent calling yinduzhanxing-skill:
theme-subset vargas, no visible audit table, and a prompt that dropped the
invocation contract. Bind the commercial method, compute D1–D60 plus Western
layers, and deliver the same Full-Spectrum checklist.
Co-authored-by: Cursor <cursoragent@cursor.com>
Activating the skill answered with the entrypoint plus a flat listing of every
file in the package, and nothing carried over between requests: the agent is
rebuilt per request, no thread memory is configured, and the replayed history is
plain question/answer text. So the model re-activated on every turn and paid for
that listing every time, twice more whenever a retry opened a fresh model loop.
The method is now read from the hash-verified package and bound into the
instructions, and the activation tools are withdrawn while skill_read stays. The
agents also stopped loading from the working-tree view, whose reference listing
no hash covered - only SKILL.md was compared - so what the model sees is finally
what the registry pinned.
Withdrawing an activation the model could forget also removes the failure it
mostly produced: the contract no longer waits on a model action for method, and
the no-birth-time path has no contract left to repair.
Measured against this package: activation 118,352 bytes, bound method 47,289.
Co-authored-by: Cursor <cursoragent@cursor.com>
Activating the skill returned 129,651 bytes, of which 99KB was a flat list of
1,592 undifferentiated file paths against 30KB of actual method. The one line
telling the model to open the strict-workflow router sat inside that method,
so no reference was ever opened and every answer was composed from the model's
own background knowledge over server evidence.
The route is already decided server-side and the skill already states which
checklist each route requires, so the selection needs no model turn: read the
mandated sections from the hash-pinned package and hand them to the model with
the evidence they apply to. A route the router declares no checklist for is
reported as such rather than filled in with another route's.
The receipt now reports delivered sections separately from model-initiated
reads, because only one of those is under the model's control.
Co-authored-by: Cursor <cursoragent@cursor.com>
Homepage visits were failing closed whenever the 30s generation missed. Return an evidence-backed card immediately, polish in the background, and remember the day's card locally.
Co-authored-by: Cursor <cursoragent@cursor.com>
The model packet read chart.modules.dasha_boundaries, a key the engine never
wrote, so no answer ever had sub-period boundaries while the receipt still
reported precise timing as allowed. The server now cuts the running mahadasha
into antardashas out of the periods the packet already shows, exposes them as
their own evidence section, and precise timing requires that section.
A run whose calculation succeeded and whose model then wrote nothing was
answered with a fixed apology and billed as completed. It now asks once more
against the cached calculation, and fails with empty_answer—no charge—if that
attempt is silent too.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep skill truth boundaries, but stop asking the model to announce skipped domains or parrot tool JSON.
Co-authored-by: Cursor <cursoragent@cursor.com>
Report pages now scroll inside the chat shell lock, and admin product save forks a draft or retires a published plan instead of rejecting with a generic constraint error.
Co-authored-by: Cursor <cursoragent@cursor.com>
Measured use of the three chips above the composer was negligible. They
were also not what they appeared to be: the server looked up a fixed
triplet by session theme and passed it as metadata that overrode
anything the model produced, so the same ten hardcoded sets served every
user regardless of question or chart. That is a plausible reason nobody
pressed them.
Both copies of the per-theme table are gone, reply metadata narrows to
the session title, and the two parse entry points collapse into one now
that they return the same shape. The write schema still tolerates a
suggestions field so a client on the previous bundle does not lose its
message mid-deploy, and stored answers containing the legacy hidden
block are still stripped rather than shown raw.
Co-authored-by: Cursor <cursoragent@cursor.com>
The home screen renders all ten domains from the consultation registry,
but the Agent only ever wrote three of them; the other seven were static
registry prompts dressed up as personalized starting points. The payload
now has to cover every domain in registry order, validated as a set
rather than per item, so a short or misordered answer is rejected whole
instead of silently leaving cards on static copy.
The greeting went the other way. Nothing has rendered it since the hero
note was removed, so it leaves the schema, the prompt, and the client
contract rather than costing tokens for text no one reads.
Ten questions take much longer to generate than three, so the route,
the server generation budget, and the client request deadline all grow
together, and the cache version bump forces existing payloads to be
regenerated once under the new shape.
Co-authored-by: Cursor <cursoragent@cursor.com>
The daily starlanguage card sat on "正在结合你的星盘写今天的星语。" forever for
every account whose birth time was usable. Its effect bailed out on
birthTimeDisplayState(profile), which returns a value precisely when the
birth time is candidate, accepted or confirmed, so the request went out
only for accounts that had nothing to read. The guard predates the Agent
rewrite and was masked by the written-in client fallback that rewrite
deleted. It now gates on personalChartAvailable, the same fact the card
uses to render personal content, and retries once before admitting that
today has no card.
The route stops letting one engine call take the whole card down
silently: /api/chart fails into a named reason like the other four
layers, and the engine and agent budgets leave room for a cold chart and
an observed 30s generation inside the 60s ceiling.
The home also had three greeting implementations. The hero heading drew
from a static pool while the time-aware greeting lived elsewhere and the
Agent's own greeting was overwritten client-side into a field nothing
rendered. createStartGreeting now exposes its salutation and question
halves, the hero uses both, and the served greeting reaches the hero note.
Co-authored-by: Cursor <cursoragent@cursor.com>
The daily starlanguage card claimed to be personal but was a four-card
rotation picked by hashing the date and birth place, with the same pool
duplicated as a client fallback. It now collects chart, Vimshottari and
Narayana dasha, D9/D10 and today's transits, hands that evidence to a
dedicated Agent, and keeps the result per account per day in process. A
failed generation says so instead of printing generic advice.
The starter heading is drawn from a pool on each visit, and the
rectification card drops its fine print.
Co-authored-by: Cursor <cursoragent@cursor.com>
A zero-uncertainty exact declaration is accepted server-side as the active
minute, but the account write only answered {ok:true}. Every save path then
kept the draft it submitted, so the first consultation after initialization
asked for unverified_birth_time against an accepted profile and was rejected
with mode_changed before billing.
The account route now returns the status and active minute it derived, and
every profile save adopts that result instead of its own local guess.
Co-authored-by: Cursor <cursoragent@cursor.com>
The single search field asked people to type a place name and then judge
which of several near-identical results was theirs, which is the one thing
they cannot verify about their own birth record. Province, city and district
are now chosen from the dataset the app already ships, so there is nothing to
type and nothing to disambiguate.
Levels that offer no choice collapse: municipalities show one level, and
prefecture cities without districts stop at the city. A district can be left
as the city centre, which is accurate enough because the chart only needs
coordinates and a timezone.
The timezone is resolved once for the chosen place rather than on every
keystroke, through a dedicated route that both this picker and the Geoapify
path share.
Co-authored-by: Cursor <cursoragent@cursor.com>
A staging consultation submitted a three-domain plan, calculated all three
successfully in 62.9s, and returned nothing but the ensureFinalResponseText
fallback. The step budget was barely touched, so this is not the exhaustion
c8d9ec64 fixed. toModelDomainPlanContext returns two different shapes: a single
domain flattens the evidence packet to the top level, several domains return only
success, domains and consultations. Every hard output rule in jyotishInstructions
is written against those top-level paths — evidence_contract.answer_policy,
hard_blockers, rectification.boundary, status. None of them resolve in the
multi-domain shape, and under a policy that forbids stating anything the server
evidence does not support, silence is what the instructions ask for.
Merge the packets into one top-level contract shaped exactly like the single
domain one. Merging may only restrict: status takes the worst of ready >
degraded > blocked, hard_blockers and missing_route_layers take the union,
permission booleans need every domain to agree while limitation booleans need
only one, and a field the domains genuinely disagree on is reported as
unresolved rather than decided. available_layers is the one permission-shaped
union, because a layer really was computed for some domain and denying it would
deny real evidence. The natal projection is the same chart for every domain, so
it is hoisted to one copy when the domains agree and left per-domain when they
do not.
The domain cap was six, advertised as six, and could never be paid for. Domains
run sequentially at ~21s each against a cumulative 110s abort signal, so six is
~126s and four leaves nothing to write the answer with. Concurrency is not
available: the Python API is a single GIL-bound ThreadingHTTPServer whose async
work already sits behind a two-worker bounded queue that answers 503 when full.
Derive the cap from the clock instead of choosing it — 110s minus a 45s answer
reserve, divided by 21s, is three — and let the model-facing schema carry that
bound so an unpayable plan is unrepresentable. A caller that builds a plan
without that schema is truncated rather than refused, the loop stops early when
the measured pace says the next domain will not fit, and either way the dropped
domains are disclosed through omitted_domains and the receipt while status
degrades, so a partial answer cannot be presented as complete.
run.failed carried a code and nothing else, so the step durations, step budget
and workflow route recorded by c8d9ec64 were unavailable exactly when a run
needed explaining. Send the same allowlisted receipt run.completed sends,
built through publicConsultationRuntimeSteps so the internal failure code and
model loop diagnostics stay server-side, and never let building it replace the
failure event with a silent close. An agentic run that fails before
streamAgentResponse exists never reached the settle-and-log path either, so the
request-level catch now goes through the same entry point.
Refs BUG-256, BUG-257, BUG-258.
Co-authored-by: Cursor <cursoragent@cursor.com>
A staging consultation calculated the chart and then returned nothing but the
ensureFinalResponseText fallback. The model had made four calls to
run-jyotish-consultation, and two of them never reached a calculation: they set
both domains and theme, which canonicalDomainPlan rejects at execution. The
schema declared those two fields as independent optionals, the description never
mentioned the constraint, and the instructions actively told the model to use
theme for a single-domain retry. Each attempt therefore bought a rule the
contract never stated, and because the throw happens before the step-recording
try/catch, it left no trace in the receipt either.
Make the constraint unrepresentable instead of enforced. The model-facing schema
keeps only question and domains, so Mastra refuses the pair before the tool body
runs; the description states the single-array contract, and the instruction that
advertised theme is gone. canonicalDomainPlan still resolves the single-value
form for callers that build a plan without that schema, and is now exported so
that path has its own tests.
maxSteps and the abort timeout bound the same run but were hard-coded apart. One
calculation takes about 20s against a 110s budget, so time is the binding
constraint and three failed calculations exhaust it whatever the step count. The
budget only has to cover the longest useful shape, so it moves to 8 beside the
timeout with that reasoning recorded, and the recorded step list is sized to
match so an exhausted run cannot truncate its own evidence.
Step exhaustion was only ever inferable by counting events, since finishReason
was recorded nowhere and progressive-disclosure reads never reach the public
stream. Capture it as a closed enum plus a step count, normalizing anything
unrecognized, and log both as controlled fields. Neither may enter the client
receipt, whose step schema is strict and would fail a successful run.
Co-authored-by: Cursor <cursoragent@cursor.com>
Every workflow fault except abort and timeout collapsed into the single
calculation_failed code, and the upstream message was discarded, so a failing
run left no evidence of whether the API rejected the call or returned a payload
that missed the response contract.
Classify failures into a closed vocabulary carried on ConsultationWorkflowError
and record it as the failureCode of the runtime step. The observability tool
call schema gains one controlled optional field; upstream error text stays out
of logs, as that contract requires. Forward request_id to the API so a run can
be aligned with its access log.
Build public receipts from an explicit allowlist. The internal failure code
must not reach the client contract, whose step schema is strict and would
otherwise reject a successful run.
Co-authored-by: Cursor <cursoragent@cursor.com>