feat(consult): deliver the route's strict method with the evidence instead of listing 1592 filenames

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>
This commit is contained in:
Jesse_Chen
2026-08-18 20:49:49 +08:00
parent 9d8b91ac02
commit ff70ba87b0
10 changed files with 365 additions and 12 deletions
+13 -1
View File
@@ -7,6 +7,7 @@ import {
type ConsultationDomain,
} from "../lib/consultation-domain-registry.ts";
import { applyBirthTimeModeToWorkflowContext, type ConsultationBirthTimeMode } from "../lib/consultation-birth-time-mode.ts";
import { consultationMethodologyForDomains } from "../lib/consultation-methodology.ts";
import type { ServerChartConsultation } from "../lib/consultation-route-service.ts";
import { createConsultationPlan, type ConsultationPlan } from "../lib/consultation-plan.ts";
import type { WorkflowReceipt } from "../lib/consultation-agent-events.ts";
@@ -90,6 +91,7 @@ export type ConsultationRuntimeStep = {
export type ConsultationRuntimeState = {
jyotishSkillLoaded: boolean;
skillReferenceReadCount: number;
methodologySectionCount: number;
consultationToolStarted: boolean;
consultationToolCompleted: boolean;
consultationToolCallCount: number;
@@ -112,6 +114,7 @@ export function createConsultationRuntimeState(options: { plannedSteps?: number;
return {
jyotishSkillLoaded: false,
skillReferenceReadCount: 0,
methodologySectionCount: 0,
consultationToolStarted: false,
consultationToolCompleted: false,
consultationToolCallCount: 0,
@@ -132,6 +135,7 @@ export function consultationModelStepTelemetry(state: ConsultationRuntimeState)
return {
modelStepCount: state.modelStepCount,
skillReferenceReads: state.skillReferenceReadCount,
methodologySections: state.methodologySectionCount,
...(state.modelFinishReason === undefined ? {} : { modelFinishReason: state.modelFinishReason }),
};
}
@@ -451,10 +455,16 @@ function toModelDomainPlanContext(
...execution.modelOutput,
}));
const success = executions.every((execution) => execution.context.success);
const methodology = consultationMethodologyForDomains(domains);
const plan = {
success,
domains,
omitted_domains: [...omittedDomains],
// The skill states which checklist each route must be read against, and the
// route is already known here, so the method travels with the evidence it
// applies to instead of depending on the model opening the right file out of
// the package listing.
...(methodology ? { methodology } : {}),
};
if (consultations.length === 1 && omittedDomains.length === 0) {
return { ...consultations[0], ...plan, consultations };
@@ -545,7 +555,9 @@ export function createConsultationTools(ctx: ConsultationAgentContext) {
ctx.state.consultationToolCompleted = true;
ctx.state.consultationToolSuccessCount += 1;
appendConsultationRuntimeStep(ctx.state, { kind: "tool", name: "run-jyotish-consultation", status: "completed", durationMs: ctx.state.consultationToolDurationMs });
return toModelDomainPlanContext(executions, omittedDomains);
const modelContext = toModelDomainPlanContext(executions, omittedDomains);
ctx.state.methodologySectionCount = modelContext.methodology?.sections.length ?? 0;
return modelContext;
} catch (error) {
ctx.state.consultationToolDurationMs = now() - startedAt;
appendConsultationRuntimeStep(ctx.state, {
+1
View File
@@ -38,6 +38,7 @@ Write in concise Simplified Chinese as a natural conversation, not a report or f
For Vedic astrology questions, load the jyotish-vedic-astrology skill before deciding which calculation tool or workflow to use. Follow the skill's method and truth boundaries, but use run-jyotish-consultation for actual chart calculations instead of inventing results. Visible chat format is owned by VISIBLE VOICE above, never by the skill's report template.
For questions that require a new chart claim, call run-jyotish-consultation before answering. Simple conversational follow-ups may use the existing context.
Select consultation domains only through the single ordered domains array of run-jyotish-consultation, whether the question covers one domain or several; omit it to accept the domain the server already selected. At most ${MAX_CONSULTATION_DOMAINS} domains may be requested in one run, because they are calculated one after another inside a fixed time budget: list them in priority order and prefer the smallest plan that answers the question, since every extra domain takes time away from writing the answer. The server canonicalizes aliases, rejects unsupported/product domains, executes each accepted domain, and returns the actual domains in the tool context and receipt. The only legal domain ids are the ones enumerated in that array's schema; the skill's methodology names strict-workflow checklists such as career-timing-strict, and those labels select techniques inside the skill, never domains for this tool. A rejected domain plan is final for this run: correct the domains once, and never re-send the same call with extra parameters.
The tool result's methodology field is the skill's own strict checklist for the routes that actually ran, quoted from the pinned skill version. Treat it as the method for this answer, not as background: work through its mandatory modules against the evidence you were given, and obey its output discipline, including any instruction to separate kinds of claim rather than merge them into one vague statement. Those sections are already delivered, so never spend a turn re-reading them; methodology.further_reading lists the references the skill names, and you may read one with skill_read only when the question needs something the delivered sections do not cover. When methodology.domains_without_strict_checklist names a domain, the skill declares no strict checklist for it: use the shared baseline and do not imply a strict route was followed. When methodology is absent, follow the skill instructions you already loaded.
The tool result always carries one top-level answer contract—status, evidence_contract, claim_cards, rectification—even when several domains ran. For a multi-domain plan that top level is the most restrictive merge of the executed domains, so obey it exactly as written and read consultations only for per-domain detail. Never treat an absent top-level field as permission to answer without a contract.
When omitted_domains is non-empty, do not answer those domains and never present the reply as covering the whole plan. Stay with what was calculated. Do not announce a skipped-domain inventory or say this round was incomplete unless the user asked about coverage.
Activity, progress, tool status, and execution receipts are server-owned. Never imitate data-jyotish-activity, activity events, tool-started/tool-completed messages, or receipts in the answer text.