@exaudeus/workrail 3.11.2 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/console/assets/index-DW78t31j.css +1 -0
- package/dist/console/assets/index-EsSXrC_a.js +28 -0
- package/dist/console/index.html +2 -2
- package/dist/di/container.js +8 -0
- package/dist/di/tokens.d.ts +1 -0
- package/dist/di/tokens.js +1 -0
- package/dist/infrastructure/session/HttpServer.js +2 -14
- package/dist/manifest.json +139 -91
- package/dist/mcp/boundary-coercion.d.ts +2 -0
- package/dist/mcp/boundary-coercion.js +73 -0
- package/dist/mcp/handler-factory.d.ts +1 -1
- package/dist/mcp/handler-factory.js +13 -6
- package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +10 -2
- package/dist/mcp/handlers/shared/request-workflow-reader.js +27 -10
- package/dist/mcp/handlers/shared/workflow-source-visibility.d.ts +3 -1
- package/dist/mcp/handlers/shared/workflow-source-visibility.js +7 -3
- package/dist/mcp/handlers/v2-execution/replay.js +25 -1
- package/dist/mcp/handlers/v2-execution/start.js +23 -17
- package/dist/mcp/handlers/v2-manage-workflow-source.d.ts +7 -0
- package/dist/mcp/handlers/v2-manage-workflow-source.js +50 -0
- package/dist/mcp/handlers/v2-workflow.js +123 -8
- package/dist/mcp/output-schemas.d.ts +393 -0
- package/dist/mcp/output-schemas.js +49 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tool-descriptions.js +20 -0
- package/dist/mcp/tools.js +6 -0
- package/dist/mcp/types/tool-description-types.d.ts +1 -1
- package/dist/mcp/types/tool-description-types.js +1 -0
- package/dist/mcp/types/workflow-tool-edition.d.ts +1 -1
- package/dist/mcp/types.d.ts +2 -0
- package/dist/mcp/v2/tool-registry.js +8 -0
- package/dist/mcp/v2/tools.d.ts +15 -0
- package/dist/mcp/v2/tools.js +8 -1
- package/dist/v2/durable-core/constants.d.ts +1 -0
- package/dist/v2/durable-core/constants.js +2 -1
- package/dist/v2/durable-core/domain/observation-builder.d.ts +4 -1
- package/dist/v2/durable-core/domain/observation-builder.js +9 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +76 -16
- package/dist/v2/durable-core/schemas/session/events.d.ts +26 -5
- package/dist/v2/durable-core/schemas/session/events.js +2 -1
- package/dist/v2/infra/in-memory/managed-source-store/index.d.ts +8 -0
- package/dist/v2/infra/in-memory/managed-source-store/index.js +33 -0
- package/dist/v2/infra/local/data-dir/index.d.ts +2 -0
- package/dist/v2/infra/local/data-dir/index.js +6 -0
- package/dist/v2/infra/local/managed-source-store/index.d.ts +15 -0
- package/dist/v2/infra/local/managed-source-store/index.js +164 -0
- package/dist/v2/infra/local/session-summary-provider/index.js +2 -0
- package/dist/v2/infra/local/workspace-anchor/index.js +1 -0
- package/dist/v2/ports/data-dir.port.d.ts +2 -0
- package/dist/v2/ports/managed-source-store.port.d.ts +25 -0
- package/dist/v2/ports/managed-source-store.port.js +2 -0
- package/dist/v2/ports/workspace-anchor.port.d.ts +3 -0
- package/dist/v2/projections/resume-ranking.d.ts +1 -0
- package/dist/v2/usecases/console-routes.js +26 -0
- package/dist/v2/usecases/console-service.js +25 -6
- package/dist/v2/usecases/console-types.d.ts +22 -1
- package/dist/v2/usecases/worktree-service.d.ts +10 -0
- package/dist/v2/usecases/worktree-service.js +136 -0
- package/package.json +1 -1
- package/workflows/adaptive-ticket-creation.json +276 -282
- package/workflows/architecture-scalability-audit.json +317 -0
- package/workflows/document-creation-workflow.json +70 -191
- package/workflows/documentation-update-workflow.json +59 -309
- package/workflows/intelligent-test-case-generation.json +37 -212
- package/workflows/personal-learning-materials-creation-branched.json +1 -21
- package/workflows/presentation-creation.json +143 -308
- package/workflows/relocation-workflow-us.json +161 -535
- package/workflows/routines/tension-driven-design.json +5 -5
- package/workflows/scoped-documentation-workflow.json +110 -181
- package/workflows/workflow-for-workflows.v2.json +21 -5
- package/dist/console/assets/index-C5C4nDs4.css +0 -1
- package/dist/console/assets/index-CSUqsoQl.js +0 -28
- package/workflows/CHANGELOG-bug-investigation.md +0 -298
- package/workflows/bug-investigation.agentic.json +0 -212
- package/workflows/bug-investigation.json +0 -112
- package/workflows/mr-review-workflow.agentic.json +0 -538
- package/workflows/mr-review-workflow.json +0 -277
|
@@ -1,415 +1,123 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "relocation-workflow-us",
|
|
3
|
-
"name": "Relocation Decision Workflow (
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "
|
|
3
|
+
"name": "US Relocation Decision Workflow (Evidence-Driven • AreaSpec • Ranked Dossier)",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "An evidence-driven relocation workflow for the United States. Discovers preferences, generates a broad candidate pool with anti-anchoring discipline, screens with strict caps, deep-dives a shortlist, and produces a ranked dossier with per-location profiles and an explainable weighted ranking.",
|
|
6
|
+
"recommendedPreferences": {
|
|
7
|
+
"recommendedAutonomy": "guided",
|
|
8
|
+
"recommendedRiskPolicy": "conservative"
|
|
9
|
+
},
|
|
6
10
|
"preconditions": [
|
|
7
11
|
"User is considering relocation within the United States (v1 scope).",
|
|
8
12
|
"Agent can research the web and/or use user-provided sources.",
|
|
9
|
-
"Agent can write files to maintain a durable paper trail
|
|
13
|
+
"Agent can write files to maintain a durable paper trail, or paste canonical content in chat if file writing is unavailable."
|
|
10
14
|
],
|
|
11
15
|
"clarificationPrompts": [
|
|
12
|
-
"Are you relocating within the US only (this v1 workflow), or do you want an international-capable version?",
|
|
13
16
|
"What is your expected timeline to move (0-3 months, 3-12 months, 12+ months)?",
|
|
14
|
-
"What is your household situation (single, couple, family with kids, multi-generational
|
|
15
|
-
"Do you need to consider a specific job market
|
|
17
|
+
"What is your household situation (single, couple, family with kids, multi-generational) and which constraints matter most?",
|
|
18
|
+
"Do you need to consider a specific job market or employer location constraint?",
|
|
16
19
|
"What is your rough budget range (housing, total monthly burn) and are you buying or renting?"
|
|
17
20
|
],
|
|
18
21
|
"metaGuidance": [
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
],
|
|
30
|
-
"functionDefinitions": [
|
|
31
|
-
{
|
|
32
|
-
"name": "writeOrPasteArtifact",
|
|
33
|
-
"definition": "When a step requires a durable artifact, attempt to write/update the file(s). If file writing is unavailable, output the full pasteable content in chat and treat that as canonical."
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"name": "captureCheckpoint",
|
|
37
|
-
"definition": "Append a 'Machine State Checkpoint' entry to `RELOCATION_DOSSIER.md` that is BOTH human-meaningful and machine-resumable.\n\nRequired fields:\n- timestamp (ISO)\n- lastCompletedStepId\n- missingDataPolicy\n- weights summary (top 3 criteria + weights)\n- candidatePoolCount, nonObviousCandidateCount, shortlistCount\n- unresolved unknowns summary (1–3 bullets)\n\nDeterministic resume payload (non-optional):\n- Paste the raw `response.state` object from the latest `workflow_next` call\n- Paste the raw `response.next.stepInstanceId` object from the latest `workflow_next` call\n\nRules:\n- Keep the last 3 checkpoints only (delete older)\n- Do not stringify the JSON objects (paste as objects)\n- If you cannot write the file: paste the full updated section in chat and treat it as canonical."
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"name": "trackClaim",
|
|
41
|
-
"definition": "For each important statement about a location (cost, taxes, schools, crime, climate, job market, etc.), record: claim, source (URL or citation), retrievedAt (date), and confidenceGrade (High/Medium/Low). If unsure, grade Low."
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "areaSpec",
|
|
45
|
-
"definition": "Represent the exact boundary of a candidate deterministically using an AreaSpec.\n\nAreaSpec fields (v1):\n- areaId: stable slug for the run (derive from candidateType + displayName + stateCodes; keep consistent)\n- displayName: human-friendly\n- candidateType: metro|city|county|custom\n- region: freeform (e.g., \"New England\", \"Mid-Atlantic\", \"Southeast\")\n- stateCodes: string[] (required)\n\nBoundary definition (by candidateType):\n- metro: { metroName: string, states: string[], definitionSource: string }\n- city: { cityName: string, stateCode: string }\n- county: { countyName: string, stateCode: string, fips?: string }\n- custom (v1 mode): { mode: \"radius\", center: { place: string, stateCode: string }, radiusMiles: number }\n\nRule: No candidate may enter candidatePool without an AreaSpec."
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"name": "normalizeCandidate",
|
|
49
|
-
"definition": "Represent each candidate consistently using: { areaId, name, region, candidateType (metro/city/county/custom), areaSpec, whyIncluded, dealbreakersPassed, unknowns, notes }.\n\n- name should match areaSpec.displayName\n- areaId must be stable (do not change mid-run)\n- unknowns is a short list of unresolved questions (strings)"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"name": "defineNonObvious",
|
|
53
|
-
"definition": "A candidate is 'non-obvious' if:\n- It is NOT in `userTopOfMind`, AND\n- It is NOT in the top-N most populous US metros list used for this run (N default 100).\n\nDo NOT use substring matching (\"contains\"/\"anchored to\") to infer obviousness for city/county/custom candidates. If you cannot deterministically map a city/county/custom boundary to an MSA in the top-N list, mark obviousness as Unknown and do not count it toward non-obvious requirements.\n\nAdditionally track `qualifyingNonObviousCandidateCount`: candidates that are non-obvious AND plausibly pass dealbreakers (based on first-pass screening signal).\n\nRecord the top-N list source and N in the dossier."
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"name": "missingDataPolicy",
|
|
57
|
-
"definition": "Explicitly choose how Unknown affects scoring: neutral, penalize, or followup_required. Apply consistently across all candidates and explain the choice in the dossier."
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"name": "antiAnchoringGate",
|
|
61
|
-
"definition": "Do not proceed to deep dives unless candidatePoolCount >= minCandidatePool AND nonObviousCandidateCount >= minNonObviousCandidates. If not met, expand the pool first."
|
|
62
|
-
}
|
|
22
|
+
"DEFAULT BEHAVIOR: self-execute with tools. Ask the user only for true preferences, real confirmations, and any external context you cannot find yourself.",
|
|
23
|
+
"V2 DURABILITY: use output.notesMarkdown and explicit context variables as the durable record. RELOCATION_DOSSIER.md and profile docs are human-facing artifacts — they are NOT required workflow memory and are never read back for routing.",
|
|
24
|
+
"ANTI-ANCHORING: generate a broad pool first; screen second; deep-dive only the shortlist. Do not deep-dive a single favorite area early.",
|
|
25
|
+
"AREA BOUNDARIES: every candidate must have an AreaSpec before entering the pool. Use areaId = <candidateType>-<slug(displayName)>-<sortedStateCodes> (e.g. metro-raleigh-durham-nc). Never switch a candidate's boundary mid-run without logging it.",
|
|
26
|
+
"CLAIMS LEDGER: every key claim about a location must include source (URL or citation), retrievedAt (date), and confidenceGrade (High/Medium/Low). If a claim cannot be sourced, grade it Low.",
|
|
27
|
+
"MISSING DATA: when a data point is unavailable, record it as Unknown and apply the chosen missingDataPolicy consistently. Never silently assume a value for an Unknown.",
|
|
28
|
+
"ARTIFACTS: try to write files (RELOCATION_DOSSIER.md, relocation-profiles/<slug>.md). If file writing is unavailable, paste full canonical content in chat and treat that as the record.",
|
|
29
|
+
"MODULES: activate only sections relevant to activeModules. Do not include placeholder sections for inactive modules.",
|
|
30
|
+
"SCREENING CAPS: first-pass screening must stay fast — dealbreakers plus top weighted criteria only, strict claim and time caps. Deep research belongs in Phase 6 deep dives.",
|
|
31
|
+
"NON-OBVIOUS CANDIDATES: non-obvious = not in userTopOfMind AND not in top-100 US metros. If you cannot map a candidate to the top-100 list deterministically, mark obviousness Unknown and do not count it toward non-obvious requirements."
|
|
63
32
|
],
|
|
64
33
|
"steps": [
|
|
65
34
|
{
|
|
66
|
-
"id": "phase-
|
|
67
|
-
"title": "Phase
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"value": "timelineToMove",
|
|
94
|
-
"message": "Must set timelineToMove"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"type": "contains",
|
|
98
|
-
"value": "householdProfile",
|
|
99
|
-
"message": "Must set householdProfile"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"type": "contains",
|
|
103
|
-
"value": "housingPlan",
|
|
104
|
-
"message": "Must set housingPlan"
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"type": "contains",
|
|
108
|
-
"value": "workConstraints",
|
|
109
|
-
"message": "Must set workConstraints"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"type": "contains",
|
|
113
|
-
"value": "geoExclusions",
|
|
114
|
-
"message": "Must set geoExclusions (can be empty)"
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"type": "contains",
|
|
118
|
-
"value": "diversityDimensions",
|
|
119
|
-
"message": "Must set diversityDimensions"
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
|
-
"requireConfirmation": true
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"id": "phase-0b-area-model-and-boundary-rules",
|
|
126
|
-
"title": "Phase 0b: Area Model & Boundary Rules (Prevent Boundary Drift)",
|
|
127
|
-
"prompt": "Lock in how candidates are defined so research and scoring are comparable.\n\n1) Define and record the AreaSpec model (use the `areaSpec()` definition).\n2) Confirm custom boundary mode (v1): `customAreaMode = radius`.\n3) Define deterministic Area ID rules (record in dossier):\n - areaId = <candidateType>-<slug(displayName)>-<sortedStateCodes>\n - Example: metro-raleigh-durham-nc\n4) Define boundary resolution rules:\n - For metro: explicitly treat as the metro area (not just the city). Record the metro definition source.\n - For city/county: record state code, and FIPS if found.\n - For custom radius: record center + radiusMiles; do not silently expand.\n\nUpdate `RELOCATION_DOSSIER.md`:\n- Boundary & Definitions (AreaSpec rules + areaId rules)\n- Aggregation & Comparability Policy (v1):\n - Prefer narrative + explicit Unknowns over false precision\n - For custom areas, allow proxy/aggregate only if clearly labeled; otherwise Unknown\n\n**Set context variables (required):**\n- areaIdRule: <string>\n\nOutput (in chat):\n- areaIdRule\n- Confirmation request: proceed with these boundary rules?",
|
|
128
|
-
"agentRole": "You are a boundary discipline enforcer. Make area definitions explicit and stable.",
|
|
129
|
-
"validationCriteria": [
|
|
130
|
-
{ "type": "contains", "value": "areaIdRule", "message": "Must set areaIdRule" },
|
|
131
|
-
{ "type": "regex", "pattern": "customAreaMode:\\s*radius", "message": "customAreaMode must be radius for v1" }
|
|
132
|
-
],
|
|
133
|
-
"requireConfirmation": true
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"id": "phase-1-preference-discovery",
|
|
137
|
-
"title": "Phase 1: Preference Discovery (Draft) + Calibration Setup",
|
|
138
|
-
"prompt": "Discover what the user cares about before searching.\n\n1) Gather constraints:\n- Hard constraints (must-have): geography constraints, climate constraints, max budget, job constraints, family constraints, health constraints.\n- Anti-goals (explicit non-goals).\n- Timeline.\n\n2) Draft preferences as:\n- Dealbreakers\n- Strong preferences\n- Mild preferences\n\n3) Create an initial weight model (draft) across the activated modules:\n- Pick top 6–10 criteria.\n- Assign weights (sum to 100).\n\nIf the user is unsure how to pick numbers, use a temporary equal-weight draft (e.g., 8 criteria → 12,12,12,12,12,12,14,14) and proceed. A later step can help derive better weights via Most/Least comparisons.\n\n**Required output format (exact keys):**\n- dealbreakers: string[]\n- geoConstraints: { includeStates?: string[], excludeStates?: string[], includeRegions?: string[], excludeRegions?: string[], timeZonesAllowed?: string[] }\n- proximityConstraints: { near?: [{ feature: string, maxDriveMinutes?: number, maxMiles?: number }] }\n- climateConstraints: { summerHeat?: low|medium|high, humidityTolerance?: low|medium|high, winterSeverityTolerance?: low|medium|high, sunshineNeed?: low|medium|high, snowIceNoGo?: boolean }\n- urbanFormPreference: { density: dense|mixed|suburban|small-town|rural, walkabilityImportance?: low|medium|high }\n- policyCultureConstraints: { mustHave?: string[], mustAvoid?: string[] }\n- weights: [{ criterion: string, weight: number }]\n- weightsCount: <number>\n- weightsSumCheck: 100\n\n4) Update `RELOCATION_DOSSIER.md`: \n- Fill Preferences (Draft)\n- Fill Constraints & Dealbreakers\n- Add initial Weight Model (Draft)\n\nKeep it generic: prefer questions about tradeoffs (e.g., \"Would you trade smaller home for better walkability?\").\n\nOutput: Draft preferences + a short list of open questions (max 5).",
|
|
139
|
-
"agentRole": "You are a facilitator eliciting preferences through tradeoffs and constraints.",
|
|
140
|
-
"validationCriteria": [
|
|
141
|
-
{
|
|
142
|
-
"type": "contains",
|
|
143
|
-
"value": "dealbreakers:",
|
|
144
|
-
"message": "Must output dealbreakers"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"type": "contains",
|
|
148
|
-
"value": "geoConstraints:",
|
|
149
|
-
"message": "Must output geoConstraints"
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
"type": "contains",
|
|
153
|
-
"value": "climateConstraints:",
|
|
154
|
-
"message": "Must output climateConstraints"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
"type": "contains",
|
|
158
|
-
"value": "urbanFormPreference:",
|
|
159
|
-
"message": "Must output urbanFormPreference"
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
"type": "contains",
|
|
163
|
-
"value": "weights:",
|
|
164
|
-
"message": "Must output weights array"
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
"type": "regex",
|
|
168
|
-
"pattern": "weightsCount:\\s*(6|7|8|9|10)",
|
|
169
|
-
"message": "weightsCount must be 6–10"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
"type": "regex",
|
|
173
|
-
"pattern": "weightsSumCheck:\\s*100",
|
|
174
|
-
"message": "weightsSumCheck must be 100"
|
|
175
|
-
}
|
|
176
|
-
],
|
|
177
|
-
"requireConfirmation": false
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
"id": "phase-1c-weights-maxdiff-optional",
|
|
181
|
-
"title": "Phase 1c: Weight Derivation Helper (MaxDiff, Optional)",
|
|
182
|
-
"prompt": "Optional helper to reduce weight-setting friction.\n\nAsk the user: \"Do you want help deriving weights using Most/Least comparisons? (yes/no)\"\n\nIf NO:\n- Set pairwiseUsed = false\n- Set maxDiffSetsCount = 0\n- Keep the existing weights from Phase 1\n\nIf YES:\n1) Build deterministic MaxDiff sets from the current criteria list (in the order they appear in `weights`).\n - Let N = weightsCount\n - If N <= 7: use 3 sets of 4 criteria\n - If N >= 8: use 4 sets of 5 criteria\n - Sets are rotations of the criteria list (no randomness):\n - set0 = first K\n - set1 = rotate left by 1, take first K\n - set2 = rotate left by 2, take first K\n - set3 = rotate left by 3, take first K (only if 4 sets)\n2) For each set, ask TWO questions:\n - \"Which is MOST important to you?\"\n - \"Which is LEAST important to you?\"\n3) Derive weights deterministically from counts:\n - raw[c] = mostCount[c] - leastCount[c]\n - shifted[c] = raw[c] - min(raw) + 1 (so all >= 1)\n - weight[c] = round(shifted[c] / sum(shifted) * 100)\n - Fix rounding drift by adjusting the largest weight to make the sum exactly 100\n - If all raw values are equal (no signal), keep original weights and note that in weightsDeltaSummary\n4) Show the derived weights and allow ONE small tweak pass:\n - User may adjust up to 2 weights; re-normalize to sum=100\n\nUpdate `RELOCATION_DOSSIER.md` Preferences section:\n- Record whether MaxDiff was used\n- Record the sets and user picks (Most/Least)\n- Record the final weights and 1–5 bullets explaining what changed\n\n**Required output format (exact keys):**\n- pairwiseUsed: true|false\n- maxDiffSetsCount: <number>\n- weights: [{ criterion: string, weight: number }]\n- weightsCount: <number>\n- weightsSumCheck: 100\n- weightsDeltaSummary: [1–5 bullets]",
|
|
183
|
-
"agentRole": "You are helping the user derive stable weights using bounded Most/Least comparisons.",
|
|
184
|
-
"validationCriteria": [
|
|
185
|
-
{ "type": "regex", "pattern": "pairwiseUsed:\\s*(true|false)", "message": "Must output pairwiseUsed" },
|
|
186
|
-
{ "type": "contains", "value": "maxDiffSetsCount", "message": "Must output maxDiffSetsCount" },
|
|
187
|
-
{ "type": "contains", "value": "weights:", "message": "Must output weights array" },
|
|
188
|
-
{ "type": "regex", "pattern": "weightsCount:\\s*(6|7|8|9|10)", "message": "weightsCount must be 6–10" },
|
|
189
|
-
{ "type": "regex", "pattern": "weightsSumCheck:\\s*100", "message": "weightsSumCheck must be 100" },
|
|
190
|
-
{ "type": "contains", "value": "weightsDeltaSummary", "message": "Must output weightsDeltaSummary" }
|
|
191
|
-
],
|
|
192
|
-
"requireConfirmation": true
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
"id": "phase-1b-calibration-deck",
|
|
196
|
-
"title": "Phase 1b: Preference Calibration Deck (Anti-Anchoring)",
|
|
197
|
-
"prompt": "Generate a calibration deck of 8–12 diverse US location archetypes (not specific cities yet). Examples: dense transit metro, college town, mountain small city, coastal mid-size, sunbelt suburb, rust-belt revival city, DC-adjacent, etc.\n\nFor each archetype:\n- 2–3 sentences describing lifestyle and typical tradeoffs\n- Who it fits / who it frustrates\n- What it implies about the weight model\n\nAsk user to:\n- Rank top 3 and bottom 3 archetypes\n- Name 1–2 surprises (\"I didn't expect to like...\")\n\nThen update `RELOCATION_DOSSIER.md`:\n- Add Calibration Findings (what changed in preferences)\n- Revise the Weight Model accordingly (weights may have been derived via MaxDiff in Phase 1c)\n\nThen revise (explicitly) any of these if calibration implies changes:\n- geoConstraints\n- climateConstraints\n- urbanFormPreference\n- proximityConstraints\n\n**Required output format (exact keys):**\n- calibrationTop3: [string, string, string]\n- calibrationBottom3: [string, string, string]\n- weightsDeltaSummary: [1–5 bullets]\n- derivedSignals: { densityLeaning: string, climateLeaning: string, regionLeaning: string, travelLeaning: string }\n- weights: [{ criterion: string, weight: number }]\n\nOutput: Updated constraints (if changed), updated weight model, and what changed because of calibration.",
|
|
198
|
-
"agentRole": "You are an anti-anchoring specialist. Use diversity to reveal latent preferences.",
|
|
199
|
-
"validationCriteria": [
|
|
200
|
-
{
|
|
201
|
-
"type": "contains",
|
|
202
|
-
"value": "calibrationTop3",
|
|
203
|
-
"message": "Must output calibrationTop3"
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
"type": "contains",
|
|
207
|
-
"value": "calibrationBottom3",
|
|
208
|
-
"message": "Must output calibrationBottom3"
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
"type": "contains",
|
|
212
|
-
"value": "weightsDeltaSummary",
|
|
213
|
-
"message": "Must output weightsDeltaSummary"
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
"type": "contains",
|
|
217
|
-
"value": "derivedSignals",
|
|
218
|
-
"message": "Must output derivedSignals"
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
"type": "contains",
|
|
222
|
-
"value": "weights:",
|
|
223
|
-
"message": "Must output updated weights array"
|
|
224
|
-
}
|
|
225
|
-
],
|
|
35
|
+
"id": "phase-1-scope-and-preferences",
|
|
36
|
+
"title": "Phase 1: Scope, Modules & Calibrated Preferences",
|
|
37
|
+
"promptBlocks": {
|
|
38
|
+
"goal": "Establish the scope and structure for this relocation search, then discover calibrated preferences and a stable weight model before any research begins.",
|
|
39
|
+
"constraints": [
|
|
40
|
+
"Do not start researching candidates yet — preferences and boundary rules must be locked first.",
|
|
41
|
+
"Activate only modules the user actually needs; do not load everything by default.",
|
|
42
|
+
"If the user is unsure about weights, offer the MaxDiff helper before finalizing."
|
|
43
|
+
],
|
|
44
|
+
"procedure": [
|
|
45
|
+
"Step 1 — Confirm scope and initialize artifacts. Confirm this is a US-only v1 relocation search. Ask for userTopOfMind (0-10 areas the user already has in mind; empty is fine). Initialize RELOCATION_DOSSIER.md with sections: User Context & Modules, Boundary & Definitions, Preferences (Draft), Constraints & Dealbreakers, Missing Data Policy, Sources Strategy, Candidate Pool, Screened Candidates, Screening Claims Ledger, Baseline Flags (Not Scored), Red Flag Gate Decisions (append-only), Shortlist, Profiles Index, Comparison & Ranking, Decision Log (append-only). Create the relocation-profiles/ directory.",
|
|
46
|
+
"Step 2 — Capture user context. Ask about and record: timelineToMove (0-3 months / 3-12 months / 12+ months), householdProfile (single / couple / family with kids / multi-generational), housingPlan (rent/buy/either and budget range), workConstraints (remote/hybrid/onsite; time zones allowed), geoExclusions (states or regions to exclude).",
|
|
47
|
+
"Step 3 — Select modules. Present the module list and activate all that apply: kids/schools, commute, transit, climate-risk, healthcare-access, career-job-market, outdoors, nightlife-arts, safety, taxes, diversity-community, disability-accessibility, amenities-errands, air-quality, noise, internet-infra. Record as activeModules.",
|
|
48
|
+
"Step 4 — Lock boundary rules. Set candidateType (default: metro; options: city, county, custom). Record the AreaSpec model: areaId = <candidateType>-<slug(displayName)>-<sortedStateCodes>. For metro candidates, record the definition source and treat the full metro area as the boundary (not just the city). For custom areas (v1), use radius mode: center (place + stateCode) + radiusMiles. Update RELOCATION_DOSSIER.md Boundary & Definitions section.",
|
|
49
|
+
"Step 5 — Elicit preferences. Ask about: hard constraints (must-have geography, climate, budget, job, family, health), anti-goals (explicit non-goals), dealbreakers. Draft 6-10 weighted criteria across active modules; weights must sum to 100. If the user is unsure, start with equal-weight draft and offer MaxDiff.",
|
|
50
|
+
"Step 6 — Optional MaxDiff weight derivation. Ask: 'Do you want help deriving weights using Most/Least comparisons?' If yes: build deterministic rotation sets (N<=7: 3 sets of 4; N>=8: 4 sets of 5). For each set ask which criterion is MOST important and which is LEAST. Derive weights: raw[c] = mostCount[c] - leastCount[c]; shifted[c] = raw[c] - min(raw) + 1; weight[c] = round(shifted[c] / sum(shifted) * 100); adjust largest weight so sum = exactly 100. Allow one small tweak pass (up to 2 weights adjusted, then re-normalize).",
|
|
51
|
+
"Step 7 — Calibration deck. Generate 8-12 diverse US location archetypes (dense transit metro, college town, mountain small city, coastal mid-size, sunbelt suburb, rust-belt revival city, DC-adjacent, etc.). For each: 2-3 sentences on lifestyle and tradeoffs, who it fits, who it frustrates. Ask the user to rank top 3 and bottom 3 and name 1-2 surprises. Update weights and constraints if calibration reveals new signal. Record derivedSignals (densityLeaning, climateLeaning, regionLeaning).",
|
|
52
|
+
"Capture these context variables: activeModules, candidateType, userTopOfMind, timelineToMove, householdProfile, housingPlan, workConstraints, geoExclusions, dealbreakers, weights (array of {criterion, weight}), weightsCount, derivedSignals."
|
|
53
|
+
],
|
|
54
|
+
"verify": [
|
|
55
|
+
"weights sum to exactly 100",
|
|
56
|
+
"activeModules is non-empty",
|
|
57
|
+
"candidateType is set",
|
|
58
|
+
"dealbreakers are explicit (not empty)",
|
|
59
|
+
"calibration deck has been shown and user has responded"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
226
62
|
"requireConfirmation": true
|
|
227
63
|
},
|
|
228
64
|
{
|
|
229
65
|
"id": "phase-2-policy-and-gates",
|
|
230
|
-
"title": "Phase 2:
|
|
231
|
-
"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
"
|
|
241
|
-
"
|
|
242
|
-
"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
"type": "contains",
|
|
256
|
-
"value": "minNonObviousCandidates",
|
|
257
|
-
"message": "Must set minNonObviousCandidates"
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
"type": "contains",
|
|
261
|
-
"value": "minCoverageRegions",
|
|
262
|
-
"message": "Must set minCoverageRegions"
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
"type": "contains",
|
|
266
|
-
"value": "minCoverageClimateBands",
|
|
267
|
-
"message": "Must set minCoverageClimateBands"
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
"type": "contains",
|
|
271
|
-
"value": "shortlistMin",
|
|
272
|
-
"message": "Must set shortlistMin"
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
"type": "contains",
|
|
276
|
-
"value": "shortlistMax",
|
|
277
|
-
"message": "Must set shortlistMax"
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
"type": "contains",
|
|
281
|
-
"value": "screeningTopCriteriaCount",
|
|
282
|
-
"message": "Must set screeningTopCriteriaCount"
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
"type": "contains",
|
|
286
|
-
"value": "screeningMaxClaimsPerCandidate",
|
|
287
|
-
"message": "Must set screeningMaxClaimsPerCandidate"
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
"type": "contains",
|
|
291
|
-
"value": "screeningTimeboxMinutesPerCandidate",
|
|
292
|
-
"message": "Must set screeningTimeboxMinutesPerCandidate"
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
"type": "contains",
|
|
296
|
-
"value": "screeningBatchSize",
|
|
297
|
-
"message": "Must set screeningBatchSize"
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
"type": "contains",
|
|
301
|
-
"value": "perSourceCandidateCap",
|
|
302
|
-
"message": "Must set perSourceCandidateCap"
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
"type": "contains",
|
|
306
|
-
"value": "baselineMaxFlagsPerCandidate",
|
|
307
|
-
"message": "Must set baselineMaxFlagsPerCandidate"
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
"type": "contains",
|
|
311
|
-
"value": "baselineMaxSourcesPerFlag",
|
|
312
|
-
"message": "Must set baselineMaxSourcesPerFlag"
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
"type": "contains",
|
|
316
|
-
"value": "baselineTimeboxMinutesPerCandidate",
|
|
317
|
-
"message": "Must set baselineTimeboxMinutesPerCandidate"
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
"type": "regex",
|
|
321
|
-
"pattern": "shortlistRangeCheck:\\s*ok",
|
|
322
|
-
"message": "Must confirm shortlistMin <= shortlistMax"
|
|
323
|
-
}
|
|
324
|
-
],
|
|
66
|
+
"title": "Phase 2: Decision Policies & Gate Parameters",
|
|
67
|
+
"promptBlocks": {
|
|
68
|
+
"goal": "Lock the decision mechanics and gate parameters before any candidate research begins. These policies govern how ambiguity, missing data, and diversity requirements are handled throughout the workflow.",
|
|
69
|
+
"constraints": [
|
|
70
|
+
"Every policy must be explicit — no implicit defaults allowed past this gate.",
|
|
71
|
+
"The user must confirm these settings before Phase 3 begins."
|
|
72
|
+
],
|
|
73
|
+
"procedure": [
|
|
74
|
+
"Step 1 — Missing data policy. Ask the user to choose one: (a) neutral — Unknown scores 0.5; (b) penalize — Unknown scores 0.25; (c) followup_required — Unknown scores 0.5 AND candidates with Unknown on any criterion with weight >= 15 are ineligible for the top 3. Record as missingDataPolicy.",
|
|
75
|
+
"Step 2 — Intake completeness check. Confirm you have enough context to set dealbreakers and weights. If not, note missingInputs and resolve before proceeding.",
|
|
76
|
+
"Step 3 — Anti-anchoring gate parameters. Propose defaults and ask the user to confirm or adjust: minCandidatePool (default 20), minNonObviousCandidates (default 6), minCoverageRegions (default 3), minCoverageClimateBands (default 2).",
|
|
77
|
+
"Step 4 — Shortlist range. Propose defaults and ask the user to confirm or adjust: shortlistMin (default 8), shortlistMax (default 12).",
|
|
78
|
+
"Step 5 — Screening caps. Propose defaults and ask the user to confirm or adjust: screeningTopCriteriaCount (default 3 — screen dealbreakers + top N weighted criteria only), screeningMaxClaimsPerCandidate (default 3), screeningTimeboxMinutesPerCandidate (default 5), screeningBatchSize (default 10).",
|
|
79
|
+
"Step 6 — Discovery caps. Propose defaults and ask the user to confirm or adjust: perSourceCandidateCap (default 8 — cap per curated-list source to avoid editorial bias).",
|
|
80
|
+
"Step 7 — Baseline flags caps. Propose defaults and ask the user to confirm or adjust: baselineMaxFlagsPerCandidate (default 2), baselineMaxSourcesPerFlag (default 1), baselineTimeboxMinutesPerCandidate (default 2).",
|
|
81
|
+
"Update RELOCATION_DOSSIER.md with all policies and caps. Capture all values as context variables: missingDataPolicy, minCandidatePool, minNonObviousCandidates, minCoverageRegions, minCoverageClimateBands, shortlistMin, shortlistMax, screeningTopCriteriaCount, screeningMaxClaimsPerCandidate, screeningTimeboxMinutesPerCandidate, screeningBatchSize, perSourceCandidateCap, baselineMaxFlagsPerCandidate, baselineMaxSourcesPerFlag, baselineTimeboxMinutesPerCandidate."
|
|
82
|
+
],
|
|
83
|
+
"verify": [
|
|
84
|
+
"missingDataPolicy is one of: neutral, penalize, followup_required",
|
|
85
|
+
"shortlistMin <= shortlistMax",
|
|
86
|
+
"all gate parameters are explicit numbers"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
325
89
|
"requireConfirmation": true
|
|
326
90
|
},
|
|
327
91
|
{
|
|
328
|
-
"id": "phase-
|
|
329
|
-
"title": "Phase
|
|
330
|
-
"
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
"
|
|
347
|
-
"
|
|
348
|
-
"
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
"type": "contains",
|
|
352
|
-
"value": "nonObviousCandidateCount",
|
|
353
|
-
"message": "Must set nonObviousCandidateCount"
|
|
354
|
-
},
|
|
355
|
-
{
|
|
356
|
-
"type": "contains",
|
|
357
|
-
"value": "qualifyingNonObviousCandidateCount",
|
|
358
|
-
"message": "Must set qualifyingNonObviousCandidateCount"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
"type": "contains",
|
|
362
|
-
"value": "coverageRegionsCount",
|
|
363
|
-
"message": "Must set coverageRegionsCount"
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
"type": "contains",
|
|
367
|
-
"value": "coverageClimateBandsCount",
|
|
368
|
-
"message": "Must set coverageClimateBandsCount"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"type": "contains",
|
|
372
|
-
"value": "discoverySourcesUsed",
|
|
373
|
-
"message": "Must record discoverySourcesUsed"
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
"type": "contains",
|
|
377
|
-
"value": "nonObviousDefinitionUsed",
|
|
378
|
-
"message": "Must record nonObviousDefinitionUsed"
|
|
379
|
-
}
|
|
380
|
-
],
|
|
381
|
-
"requireConfirmation": false
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
"id": "phase-3b-anti-anchoring-gate-check",
|
|
385
|
-
"title": "Phase 3b: Anti-Anchoring Gate Check",
|
|
386
|
-
"prompt": "Run antiAnchoringGate() deterministically using these comparisons:\n- candidatePoolCount >= minCandidatePool\n- qualifyingNonObviousCandidateCount >= minNonObviousCandidates\n- coverageRegionsCount >= minCoverageRegions\n- coverageClimateBandsCount >= minCoverageClimateBands\n\nIf the gate fails:\n- Expand the pool until it passes by filling coverage gaps first (diversify; avoid adding only obvious metros).\n- Recompute candidatePoolCount, qualifyingNonObviousCandidateCount, coverageRegionsCount, and coverageClimateBandsCount.\n\nIf the gate passes:\n- Proceed.\n\n**Required output format (exact keys):**\n- antiAnchoringGate: pass|fail\n- gateFailureReason: <string or empty>\n- poolExpansionCount: <number>\n\nUpdate `RELOCATION_DOSSIER.md` with gate status and any expansions performed.",
|
|
387
|
-
"agentRole": "You enforce anti-anchoring and minimum diversity requirements.",
|
|
388
|
-
"validationCriteria": [
|
|
389
|
-
{
|
|
390
|
-
"type": "regex",
|
|
391
|
-
"pattern": "antiAnchoringGate:\\s*(pass|fail)",
|
|
392
|
-
"message": "Must output antiAnchoringGate: pass|fail"
|
|
393
|
-
}
|
|
394
|
-
],
|
|
395
|
-
"requireConfirmation": false
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
"id": "phase-3c-build-screening-batches",
|
|
399
|
-
"title": "Phase 3c: Build Screening Batches (Chunking)",
|
|
400
|
-
"prompt": "Prepare chunked screening to avoid excessively large loop iteration limits.\n\nGoal: build `screeningBatches` from `candidatePool` using `screeningBatchSize`.\n\nRules:\n- Preserve candidate order from `candidatePool`.\n- Each batch has at most `screeningBatchSize` candidates.\n- Each batch must be represented deterministically as:\n - { batchId, startIndex, endIndexExclusive, candidates }\n - where candidates is the list of normalized candidates (or their names/areaIds), in order.\n\n**Set context variables (required):**\n- screeningBatches: array\n- screeningBatchesCount: number\n\nUpdate `RELOCATION_DOSSIER.md`:\n- Add a short note under \"Screened Candidates\" explaining batching (batch size + number of batches).\n\n**Required output format (exact keys):**\n- screeningBatchSize: <number>\n- screeningBatchesCount: <number>\n- screeningBatches: <present>\n\nThen proceed to Phase 4.",
|
|
401
|
-
"agentRole": "You are preparing chunked execution. Keep the screening loop bounded and resumable.",
|
|
402
|
-
"validationCriteria": [
|
|
403
|
-
{ "type": "contains", "value": "screeningBatches:", "message": "Must output screeningBatches" },
|
|
404
|
-
{ "type": "contains", "value": "screeningBatchesCount", "message": "Must output screeningBatchesCount" },
|
|
405
|
-
{ "type": "contains", "value": "screeningBatchSize", "message": "Must output screeningBatchSize" }
|
|
406
|
-
],
|
|
92
|
+
"id": "phase-3-breadth-and-gate",
|
|
93
|
+
"title": "Phase 3: Breadth Search, Anti-Anchoring Gate & Screening Batches",
|
|
94
|
+
"promptBlocks": {
|
|
95
|
+
"goal": "Generate a broad, diverse candidate pool that satisfies the anti-anchoring gate, then build the screening batches for Phase 4.",
|
|
96
|
+
"constraints": [
|
|
97
|
+
"Every candidate must have an AreaSpec before entering the pool.",
|
|
98
|
+
"Cap contributions from any single curated-list source to perSourceCandidateCap to avoid editorial bias.",
|
|
99
|
+
"Do not proceed to screening until the anti-anchoring gate passes."
|
|
100
|
+
],
|
|
101
|
+
"procedure": [
|
|
102
|
+
"Step 1 — Sources strategy. Before generating candidates, document the sources strategy in RELOCATION_DOSSIER.md: Housing (Zillow + alternative), Taxes (state revenue sites), Climate normals (NOAA), Climate risk (FEMA flood maps), Employment (BLS / state labor stats), Transit/commute (local agencies), Air quality (AirNow/EPA), Noise (airport contour maps), Internet (FCC broadband map). Include only sources for active modules. Use this sources strategy as your research guide throughout candidate generation — generate the pool from actual data, not from memory alone.",
|
|
103
|
+
"Step 2 — Generate candidates. Use the weight model and dealbreakers as the filter. For each candidate: assign a stable areaId, record the full AreaSpec (candidateType, displayName, stateCodes, and boundary definition), record why included, tag with candidateFacets (region, climateBand, sizeTier, taxRegime, airportAccess, outdoorsBiome as applicable). Fill coverage gaps deliberately — include a mix of obvious and non-obvious candidates. Include at least minCandidatePool candidates total.",
|
|
104
|
+
"Step 3 — Anti-anchoring gate. Check: candidatePoolCount >= minCandidatePool, qualifyingNonObviousCandidateCount >= minNonObviousCandidates, coverageRegionsCount >= minCoverageRegions, coverageClimateBandsCount >= minCoverageClimateBands. A 'qualifying non-obvious' candidate is non-obvious AND plausibly passes dealbreakers. Record the top-100 list source used for non-obvious classification. If the gate fails, expand the pool by filling coverage gaps (prefer non-obvious candidates). Repeat until the gate passes.",
|
|
105
|
+
"Step 4 — Build screening batches. Divide candidatePool into batches of screeningBatchSize, preserving order. Each batch: { batchId, startIndex, endIndexExclusive, candidates }. Record screeningBatches and screeningBatchesCount.",
|
|
106
|
+
"Update RELOCATION_DOSSIER.md: Candidate Pool table (name, candidateType, region, why included, early risks/unknowns). Capture context variables: candidatePool, candidatePoolCount, nonObviousCandidateCount, qualifyingNonObviousCandidateCount, coverageRegionsCount, coverageClimateBandsCount, screeningBatches, screeningBatchesCount, discoverySourcesUsed, nonObviousDefinitionUsed."
|
|
107
|
+
],
|
|
108
|
+
"verify": [
|
|
109
|
+
"anti-anchoring gate passed",
|
|
110
|
+
"every candidate has an AreaSpec",
|
|
111
|
+
"screeningBatches is non-empty",
|
|
112
|
+
"discoverySourcesUsed is recorded"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
407
115
|
"requireConfirmation": false
|
|
408
116
|
},
|
|
409
117
|
{
|
|
410
118
|
"id": "phase-4-screening-loop",
|
|
411
119
|
"type": "loop",
|
|
412
|
-
"title": "Phase 4: First-Pass Screening
|
|
120
|
+
"title": "Phase 4: First-Pass Screening",
|
|
413
121
|
"loop": {
|
|
414
122
|
"type": "forEach",
|
|
415
123
|
"items": "screeningBatches",
|
|
@@ -419,188 +127,106 @@
|
|
|
419
127
|
},
|
|
420
128
|
"body": [
|
|
421
129
|
{
|
|
422
|
-
"id": "phase-
|
|
130
|
+
"id": "phase-4-screen-batch",
|
|
423
131
|
"title": "Screen Batch {{batchIndex}}",
|
|
424
|
-
"prompt": "
|
|
425
|
-
"agentRole": "You are doing triage-level screening in batches to keep the workflow scalable and resumable.",
|
|
426
|
-
"validationCriteria": [
|
|
427
|
-
{
|
|
428
|
-
"type": "contains",
|
|
429
|
-
"value": "batchId",
|
|
430
|
-
"message": "Must output batchId"
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
"type": "contains",
|
|
434
|
-
"value": "batchScreenedCount",
|
|
435
|
-
"message": "Must output batchScreenedCount"
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
"type": "contains",
|
|
439
|
-
"value": "batchPassCount",
|
|
440
|
-
"message": "Must output batchPassCount"
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
"type": "contains",
|
|
444
|
-
"value": "batchFailCount",
|
|
445
|
-
"message": "Must output batchFailCount"
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
"type": "contains",
|
|
449
|
-
"value": "batchMaybeCount",
|
|
450
|
-
"message": "Must output batchMaybeCount"
|
|
451
|
-
}
|
|
452
|
-
],
|
|
453
|
-
"requireConfirmation": false
|
|
132
|
+
"prompt": "Screen the current batch with a fast, high-signal pass.\n\nBatch: { batchId, startIndex, endIndexExclusive, candidates }\n\nFor each candidate in batch.candidates (do not skip):\n- Screen only: dealbreakers + top screeningTopCriteriaCount weighted criteria\n- Record at most screeningMaxClaimsPerCandidate claims per candidate in the Screening Claims Ledger\n- Prefer 1 source per claim; mark Unknown if unavailable\n- Stay within screeningTimeboxMinutesPerCandidate per candidate\n- Assign a screenResult: Pass, Fail, or Maybe\n\nAfter all candidates in the batch:\n- Update RELOCATION_DOSSIER.md Screened Candidates table (add each result)\n- Append batch summary to Screening Claims Ledger (batchId, screened count, pass/fail/maybe counts, repeated unknown categories)\n- Update screenResults context map: { [candidateName]: 'Pass' | 'Fail' | 'Maybe' }"
|
|
454
133
|
}
|
|
455
134
|
]
|
|
456
135
|
},
|
|
457
136
|
{
|
|
458
|
-
"id": "phase-
|
|
459
|
-
"title": "Phase
|
|
460
|
-
"prompt": "
|
|
461
|
-
"agentRole": "You are doing bounded baseline due diligence without affecting scoring.",
|
|
462
|
-
"validationCriteria": [
|
|
463
|
-
{ "type": "contains", "value": "baselineFlags", "message": "Must set baselineFlags" },
|
|
464
|
-
{ "type": "contains", "value": "redFlagCandidates", "message": "Must set redFlagCandidates" },
|
|
465
|
-
{ "type": "contains", "value": "redFlagCount", "message": "Must set redFlagCount" }
|
|
466
|
-
],
|
|
137
|
+
"id": "phase-4-baseline-flags",
|
|
138
|
+
"title": "Phase 4b: Baseline Due Diligence (Not Scored)",
|
|
139
|
+
"prompt": "Run a lightweight baseline pass on all Pass or Maybe candidates from screenResults.\n\nScope — check only:\n- Climate risk (high-level: flood zone, wildfire, extreme heat)\n- Safety and crime (high-level: neighborhood-level variance)\n- Schools and healthcare access — only if kids/schools or healthcare-access modules are active\n\nCaps (apply strictly):\n- At most baselineMaxFlagsPerCandidate flags per candidate\n- At most baselineMaxSourcesPerFlag sources per flag\n- At most baselineTimeboxMinutesPerCandidate minutes per candidate\n\nFor each Pass/Maybe candidate, produce 0 to baselineMaxFlagsPerCandidate baseline flags. Each flag: category (climate/safety/schools/healthcare/policy/other), severity (yellow/orange/red), one-sentence summary, source, retrievedAt, confidenceGrade. A red flag has severity = red.\n\nDo NOT compute or modify any scores here. Do NOT silently turn flags into dealbreakers or weights. If evidence is unclear, record Unknown.\n\nUpdate RELOCATION_DOSSIER.md: add Baseline Flags (Not Scored) section with per-candidate table.\n\nCapture context variables: baselineFlags ({ [candidateKey]: { flags: array, unknowns: string[] } }), redFlagCandidates (string[]), redFlagCount (number).",
|
|
467
140
|
"requireConfirmation": false
|
|
468
141
|
},
|
|
469
142
|
{
|
|
470
|
-
"id": "phase-
|
|
471
|
-
"title": "Phase
|
|
472
|
-
"
|
|
473
|
-
"agentRole": "You enforce explicit user intent for red flags (no hidden weighting).",
|
|
474
|
-
"validationCriteria": [
|
|
143
|
+
"id": "phase-4-red-flag-gate",
|
|
144
|
+
"title": "Phase 4c: Red Flag Gate",
|
|
145
|
+
"promptFragments": [
|
|
475
146
|
{
|
|
476
|
-
"
|
|
477
|
-
"
|
|
478
|
-
"
|
|
147
|
+
"id": "no-red-flags",
|
|
148
|
+
"when": { "var": "redFlagCount", "equals": 0 },
|
|
149
|
+
"text": "No red flags were detected. Record redFlagDecision = 'fyi' and redFlagDecisionNotes = 'No red flags detected in baseline due diligence.' Proceed to Phase 5."
|
|
479
150
|
},
|
|
480
151
|
{
|
|
481
|
-
"
|
|
482
|
-
"
|
|
483
|
-
"
|
|
152
|
+
"id": "has-red-flags",
|
|
153
|
+
"when": { "var": "redFlagCount", "gt": 0 },
|
|
154
|
+
"text": "Red flags were found. Summarize each: candidate name, category, one-line summary, source. Ask the user to choose exactly one action: (a) promote_to_dealbreakers — update the `dealbreakers` context variable AND the RELOCATION_DOSSIER.md Constraints section with the new/updated dealbreakers, then re-check screenResults for affected candidates; (b) add_weighted_criterion — ask the user how to weight it and which existing weights decrease so the `weights` array still sums to 100; (c) fyi — record the decision and move on. Record redFlagDecision and redFlagDecisionNotes. Append to RELOCATION_DOSSIER.md Red Flag Gate Decisions (append-only)."
|
|
484
155
|
}
|
|
485
156
|
],
|
|
157
|
+
"prompt": "Handle baseline red flags before selecting the shortlist.",
|
|
486
158
|
"requireConfirmation": { "var": "redFlagCount", "gt": 0 }
|
|
487
159
|
},
|
|
488
160
|
{
|
|
489
|
-
"id": "phase-
|
|
490
|
-
"title": "Phase
|
|
491
|
-
"
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
"
|
|
496
|
-
"
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
"
|
|
501
|
-
"
|
|
502
|
-
"
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
"
|
|
506
|
-
"
|
|
507
|
-
"
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
"type": "contains",
|
|
511
|
-
"value": "shortlistNonObviousCount",
|
|
512
|
-
"message": "Must set shortlistNonObviousCount"
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
"type": "contains",
|
|
516
|
-
"value": "shortlistRedFlagCount",
|
|
517
|
-
"message": "Must set shortlistRedFlagCount"
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
"type": "regex",
|
|
521
|
-
"pattern": "shortlistRangeCheck:\\s*ok",
|
|
522
|
-
"message": "Must confirm shortlistMin <= shortlistCount <= shortlistMax"
|
|
523
|
-
}
|
|
524
|
-
],
|
|
161
|
+
"id": "phase-5-shortlist",
|
|
162
|
+
"title": "Phase 5: Shortlist Selection",
|
|
163
|
+
"promptBlocks": {
|
|
164
|
+
"goal": "Select a diverse, curated shortlist of candidates for deep dives.",
|
|
165
|
+
"constraints": [
|
|
166
|
+
"Shortlist must be within shortlistMin to shortlistMax candidates.",
|
|
167
|
+
"Must include at least 3 candidates outside userTopOfMind (if provided).",
|
|
168
|
+
"Baseline flags do not affect scores, but the shortlist rationale must call out any red/orange flags for shortlisted candidates."
|
|
169
|
+
],
|
|
170
|
+
"procedure": [
|
|
171
|
+
"Review screenResults: all Pass and Maybe candidates are eligible. If too many pass, prefer diversity across archetypes, regions, and climate bands over ranking by screening signal.",
|
|
172
|
+
"For each shortlisted candidate, write a short rationale: why included, any notable red/orange baseline flags, what makes it distinct.",
|
|
173
|
+
"Update RELOCATION_DOSSIER.md: Shortlist section (rationale per candidate), Profiles Index (planned profile file paths).",
|
|
174
|
+
"Capture context variables: shortlist (normalized candidates array), shortlistCount, shortlistNonTopOfMindCount, shortlistNonObviousCount, shortlistRedFlagCount."
|
|
175
|
+
],
|
|
176
|
+
"verify": [
|
|
177
|
+
"shortlistCount is between shortlistMin and shortlistMax",
|
|
178
|
+
"shortlistNonTopOfMindCount >= 3 (or all shortlisted candidates if userTopOfMind is empty)",
|
|
179
|
+
"every shortlisted candidate has a rationale"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
525
182
|
"requireConfirmation": true
|
|
526
183
|
},
|
|
527
184
|
{
|
|
528
|
-
"id": "phase-
|
|
529
|
-
"title": "Phase 4c: Checkpoint (After Shortlist Confirmation)",
|
|
530
|
-
"prompt": "Run captureCheckpoint() and append a Machine State Checkpoint entry to `RELOCATION_DOSSIER.md`.\n\nRequired:\n- record lastCompletedStepId = phase-4b-select-shortlist\n- include the raw `response.state` and `response.next.stepInstanceId` objects from the latest `workflow_next` call",
|
|
531
|
-
"agentRole": "You are maintaining resumability. Capture enough state to resume deterministically.",
|
|
532
|
-
"requireConfirmation": false
|
|
533
|
-
},
|
|
534
|
-
{
|
|
535
|
-
"id": "phase-5-profile-deep-dive-loop",
|
|
185
|
+
"id": "phase-6-deep-dive-loop",
|
|
536
186
|
"type": "loop",
|
|
537
|
-
"title": "Phase
|
|
187
|
+
"title": "Phase 6: Deep Dives (Per-Candidate Profiles)",
|
|
538
188
|
"loop": {
|
|
539
189
|
"type": "forEach",
|
|
540
190
|
"items": "shortlist",
|
|
541
191
|
"itemVar": "shortCandidate",
|
|
542
192
|
"indexVar": "shortIndex",
|
|
543
|
-
"maxIterations":
|
|
193
|
+
"maxIterations": 20
|
|
544
194
|
},
|
|
545
195
|
"body": [
|
|
546
196
|
{
|
|
547
|
-
"id": "phase-
|
|
548
|
-
"title": "Profile
|
|
549
|
-
"prompt": "Create
|
|
550
|
-
"agentRole": "You are a meticulous researcher producing consistent, evidence-backed location profiles.",
|
|
551
|
-
"validationCriteria": [
|
|
552
|
-
{
|
|
553
|
-
"type": "contains",
|
|
554
|
-
"value": "## Claims & Sources",
|
|
555
|
-
"message": "Profile must include '## Claims & Sources'"
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
"type": "contains",
|
|
559
|
-
"value": "## Unknowns & follow-ups",
|
|
560
|
-
"message": "Profile must include '## Unknowns & follow-ups'"
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
"type": "contains",
|
|
564
|
-
"value": "## Baseline Flags (Not Scored)",
|
|
565
|
-
"message": "Profile must include '## Baseline Flags (Not Scored)'"
|
|
566
|
-
}
|
|
567
|
-
],
|
|
568
|
-
"requireConfirmation": false
|
|
197
|
+
"id": "phase-6-write-profile",
|
|
198
|
+
"title": "Profile: {{shortCandidate.name}}",
|
|
199
|
+
"prompt": "Create or update the per-candidate profile at relocation-profiles/<candidate-slug>.md.\n\nStart with the boundary:\n- CandidateType and AreaSpec (exact boundary)\n\nModule-driven rule: include a section only if its module is active in activeModules. Omit sections for inactive modules entirely (no placeholders).\n\nProfile structure:\n- Summary (who it fits / who it doesn't)\n- Housing (rent/buy ranges, inventory, neighborhood variation)\n- Cost of living (beyond housing)\n- Taxes (income/property/sales; major gotchas)\n- Safety (high-level + neighborhood variance; avoid false precision)\n- Schools/childcare [kids/schools module]\n- Commute/transit [commute, transit modules]\n- Healthcare access [healthcare-access module]\n- Climate & climate risk [climate-risk module]\n- Job market [career-job-market module]\n- Lifestyle [outdoors, nightlife-arts, diversity-community modules]\n- Amenities & errands [amenities-errands module]\n- Air quality [air-quality module]\n- Noise [noise module]\n- Internet & infrastructure [internet-infra module]\n- Pros / Cons (evidence-backed)\n\nRequired sections (always include):\n## Baseline Flags (Not Scored)\nCopy baseline flags from Phase 4b for this candidate. If absent or stale, do a quick refresh within active modules (stay within caps). State explicitly: 'These baseline flags do not affect scoring unless the user chooses to promote them.'\n\n## Unknowns & Follow-ups\nList unresolved questions. Apply missingDataPolicy to any Unknowns that will affect scoring.\n\n## Claims & Sources\nEvery key claim: { claim, source (URL or citation), retrievedAt, confidenceGrade (High/Medium/Low) }. For proxy/aggregate claims (especially custom areas), label them as such.\n\nAlso add a short entry for this candidate in RELOCATION_DOSSIER.md (1 paragraph, link to profile, key differentiators)."
|
|
569
200
|
}
|
|
570
201
|
]
|
|
571
202
|
},
|
|
572
203
|
{
|
|
573
|
-
"id": "phase-
|
|
574
|
-
"title": "Phase
|
|
575
|
-
"
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
"
|
|
580
|
-
"
|
|
581
|
-
"
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
"
|
|
585
|
-
"
|
|
586
|
-
"
|
|
587
|
-
|
|
588
|
-
|
|
204
|
+
"id": "phase-7-rank-and-next-steps",
|
|
205
|
+
"title": "Phase 7: Comparison, Ranking & Next Steps",
|
|
206
|
+
"promptBlocks": {
|
|
207
|
+
"goal": "Produce the final comparison matrix, explainable ranking, and a practical next-steps plan.",
|
|
208
|
+
"constraints": [
|
|
209
|
+
"The score formula must be applied consistently to every candidate.",
|
|
210
|
+
"Unknowns must be disclosed in the ranking narrative — never presented as if they were evidence-backed.",
|
|
211
|
+
"Baseline flags (not scored) are shown separately and do not change totalScore.",
|
|
212
|
+
"One re-weight pass is allowed if the user says the ranking direction is wrong."
|
|
213
|
+
],
|
|
214
|
+
"procedure": [
|
|
215
|
+
"Step 1 — Comparison matrix. Build a table in RELOCATION_DOSSIER.md: rows = shortlisted candidates, columns = weighted criteria. Mark Unknowns explicitly. Add a separate appendix table for baseline flags (Not Scored): red/orange flags per candidate.",
|
|
216
|
+
"Step 2 — Score each candidate. For each criterion, assign a normalized subscore: Strong fit = 1.0, Mixed/conditional = 0.5, Weak fit = 0.0. For Unknowns: neutral policy → 0.5; penalize policy → 0.25; followup_required policy → 0.5 and flag candidate ineligible for top 3 if Unknown on any criterion with weight >= 15. Compute totalScore = sum(weight_i * subscore_i) for each candidate.",
|
|
217
|
+
"Step 3 — Ranking narrative. For each candidate write: 'Ranks #k because it wins on X and Y, loses on Z. Biggest tradeoff: ...' Make sure all Unknown subscores are called out explicitly in the narrative.",
|
|
218
|
+
"Step 4 — Re-weight gate. Ask the user: 'Does this ranking direction feel correct?' If not, allow one re-weight (user adjusts any number of criteria weights; must still sum to 100; re-run scoring). Record reweightUsed (true/false). Update Decision Log with any weight changes and rationale.",
|
|
219
|
+
"Step 5 — Next steps. Produce: suggested visit plan for top 2-4 candidates (what to validate in person), open questions per candidate (from Unknowns sections), pivot triggers (what evidence would change the ranking), optional neighborhood-level follow-ups if enough evidence exists. Update RELOCATION_DOSSIER.md with Next Steps and Pivot Triggers.",
|
|
220
|
+
"Capture context variables: ranking ([{name, totalScore, rank}]), unknownsImpactSummary, reweightUsed."
|
|
221
|
+
],
|
|
222
|
+
"verify": [
|
|
223
|
+
"every shortlisted candidate appears in the ranking",
|
|
224
|
+
"all Unknown subscores are disclosed in the narrative",
|
|
225
|
+
"reweightUsed is set",
|
|
226
|
+
"next steps are specific to each top candidate"
|
|
227
|
+
]
|
|
228
|
+
},
|
|
589
229
|
"requireConfirmation": true
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
"id": "phase-6b-checkpoint",
|
|
593
|
-
"title": "Phase 6b: Checkpoint (After Ranking)",
|
|
594
|
-
"prompt": "Run captureCheckpoint() and append a Machine State Checkpoint entry to `RELOCATION_DOSSIER.md`.\n\nRequired:\n- record lastCompletedStepId = phase-6-compare-and-rank\n- include the raw `response.state` and `response.next.stepInstanceId` objects from the latest `workflow_next` call",
|
|
595
|
-
"agentRole": "You are maintaining resumability. Capture enough state to resume deterministically.",
|
|
596
|
-
"requireConfirmation": false
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
"id": "phase-7-next-steps",
|
|
600
|
-
"title": "Phase 7: Next Steps (Validation in the Real World)",
|
|
601
|
-
"prompt": "Create a practical next-steps plan.\n\nInclude:\n- Suggested visit plan for top 2–4 candidates (what to validate in person)\n- Open questions per candidate (from Unknowns)\n- What would change your mind (pivot triggers)\n- Optional: recommended neighborhoods to investigate further (if you have enough evidence; otherwise mark Unknown)\n\nUpdate `RELOCATION_DOSSIER.md` with Next Steps and Pivot Triggers.\n\nOutput: concise next-steps checklist.",
|
|
602
|
-
"agentRole": "You are a pragmatic planner. Translate analysis into actionable validation steps.",
|
|
603
|
-
"requireConfirmation": false
|
|
604
230
|
}
|
|
605
231
|
]
|
|
606
232
|
}
|