Parallax make house view
Skill bencharoenwong/parallax-workflows/skills/parallax-make-house-view
Synthesize a draft Parallax house view by orchestrating macro_analyst + get_telemetry MCP outputs across the 14 covered markets, route the draft through the shared confirmation gate, and save through the same path /parallax-load-house-view uses. The synthesized view carries `generator_synthesis` provenance (distinct from ingested views' `prose_extraction`) and lands in `~/.parallax/active-house-view/` like any other house view. Use when the bank has no CIO take to ingest, when prepping a Parallax baseline for shadow-comparison against an active bank view, or to populate a fresh deployment. NOT for ingesting an existing CIO PDF (use /parallax-load-house-view), not for stress-testing an active view (use /parallax-stress-house-view), not for judging an active view against current Parallax signals (use /parallax-judge-house-view). Pass --compare <view_a> <view_b> to diff two saved view bundles cell-by-cell (e.g. UBS vs Goldman) with no synthesis.From its SKILL.md
npx -y skills add bencharoenwong/parallax-workflows --skill parallax-make-house-viewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
16.8 KB, as published. Nobody here has run it
Make House View
When not to use
- Ingesting a CIO PDF / URL / wizard input → use /parallax-load-house-view
- Judging an active view against live signals → use /parallax-judge-house-view
- Stress-testing for internal consistency → use /parallax-stress-house-view
- Per-cell tilt diff of two saved views (e.g. UBS vs Goldman) → use /parallax-make-house-view --compare
- Diff of two portfolio outputs (view vs baseline) → use /parallax-house-view-diff
Gotchas
- JIT-load
_parallax/house-view/MCP_FIELD_INVENTORY.mdBEFORE assuming any component input is available.valuation_state/market_entropyare prose-extracted;psychological_wavelengthis LLM-judged. Confidence caps differ per component. - The shared gate (
_parallax/house-view/gate_present.py) is REQUIRED — there is no save path that bypasses it.--shadow-diffskips the gate AND the save. --compareis a pure structural file diff of two saved bundles: it skips MCP, synthesis, the gate, the save, the audit row, AND all output disclaimers. It never emits a pillar-confidence block (keep pillar internals out of any cross-firm output).- Fan-out budget is 14 markets × 4 components = 56 macro_analyst calls + 1 list_macro_countries + 1 get_telemetry. Concurrency cap 8. Per-market timeout 45s. Hard abort when unreachable_share > 30%.
- Reuse
audit_chain.append_entry,chain_emit.emit_phase_0_chain,provenance_classes.validate_provenance_entry. NEVER reimplement. - The
generateaudit row carries ONLY: schema_version, ts, view_id, version_id, view_hash, skill, action, applied, parent_version_id, provenance_hash, source_tools, calibration_status. composition_formula / aggregator_weights_ref / source_snippets / pillar_missing_inputs MUST go inprovenance.yaml, NOT on the audit row. psychological_wavelengthjudgment is Claude-only (data perimeter — telemetry contains CG-proprietary signals, do NOT dispatch to external models).- Synthesized views carry
auto_expire_days: 30(shorter than ingested views' 90) because the underlying macro fan-out is point-in-time. - §9.2 exemption: this skill does NOT render the AI-interaction disclosure banner. It emits configuration artifacts (
view.yaml+prose.md) whose LLM-synthesized content is gated by the Step 7 operator confirmation gate before any downstream consumer can load the view — and every downstream consumer renders §9.2 in its own output per loader.md §5 rule 6. Rationale and conditions:parallax-conventions.md§9.2 "Exemption". Registered in_NINE_TWO_EXEMPT_SKILLSin the white-label test gate.
Synthesize a draft Parallax house view by orchestrating Parallax MCP tools (list_macro_countries + macro_analyst × 14 markets × 4 components + get_telemetry), aggregate cross-country, compose the four framework components (macro backdrop, valuation, market state, sentiment), route through the shared confirmation gate, and save through the same path /parallax-load-house-view uses.
The synthesized view lives in ~/.parallax/active-house-view/ and is consumed by every downstream skill (portfolio-builder, rebalance, thematic-screen, etc.) exactly like an ingested view — the only difference is the provenance class (generator_synthesis vs prose_extraction) and a shorter default auto_expire_days.
Usage
/parallax-make-house-view # synthesize, route to gate, save
/parallax-make-house-view --shadow-diff # synthesize, DO NOT save; diff vs active view
/parallax-make-house-view --basis "<hint>" # bias synthesis with a textual hint
/parallax-make-house-view --markets us,japan,uk # restrict fan-out scope (cost / debug)
/parallax-make-house-view --status # show last-generated view metadata
/parallax-make-house-view --compare <path_a> <path_b> # diff two saved view bundles; no MCP, no synthesis, no save
Cost: ~282 tokens at the default market set (~14 markets × 4 components; see _parallax/token-costs.md); --markets scales cost proportionally.
Workflow
Call ToolSearch with query "+Parallax" to load the deferred MCP tool schemas before the first mcp__claude_ai_Parallax__* call. JIT-load _parallax/house-view/MCP_FIELD_INVENTORY.md (per-pillar input availability), _parallax/house-view/schema.yaml (view shape + classification_taxonomy.generator_synthesis), and _parallax/house-view/loader.md (esp. §6 audit format with the new generate action).
Step 1 — Resolve covered markets
Call list_macro_countries(). Expect 15 entries (14 country reports + "Global"). Exclude "Global" from per-country fan-out (it's an aggregate, would double-count). When --markets filter is set, intersect with the live coverage.
Fallback: if list_macro_countries fails, use the hardcoded HARDCODED_COVERAGE tuple in maker.py (14 markets matching aggregator_weights.yaml).
Step 2 + 3 — Parallel fan-out
Fire in parallel (concurrency cap 8, per-call timeout 45s):
- Step 2 (batch):
macro_analyst(market=M, component=C)for every (M, C) pair where M ∈ covered_markets and C ∈ {macro_indicators,tactical,sectors,news}. Budget: 14 × 4 = 56 calls. (fixed_incomeis deferred — no v0 formula consumes it; re-add it in the same change that lands a rates leg inpillar_formulas.py.) - Step 3 (single call, parallel with Step 2):
get_telemetry(fields=["regime_tag", "divergences", "factor_view.factors", "factor_view.commentary", "signals", "commentary"]).
Per-market timeout: 45s. A market is UNREACHABLE when no component returns a response whose shape can be interpreted — absent, not a mapping, or carrying neither a success nor an error key. That rule is _parallax/house-view/mcp_meta.py (carries_data), the same predicate backing stress.classify_mcp_meta_state's UNREACHABLE branch, so the maker and the stress classifier cannot disagree about what counts as a dead market. A market that returns a successful response with content like "Sector ranking data remains unavailable for this reporting period" — or an explicit success: false — is treated as silent for that component, not UNREACHABLE for the market (per MCP_FIELD_INVENTORY.md §5.4): the server answered, and only an uninterpretable shape fails closed.
If unreachable_share > 0.30 of the fan-out, HARD ABORT. Aggregation can't recover from that level of degradation. <!-- rationale: notes/2026-05-24-house-view-v2-plan.md §2.2 -->
Step 4 — Cross-country aggregation
Call cross_country.aggregate(per_market_responses, telemetry, weights). Outputs:
phi.value/phi.coverage_ok/phi.markets_with_data: weighted-median of per-marketvaluation_stateprose-extracted values when ≥ 60% of weight responded; else NULL + coverage_warning.xi.valueetc.: same shape formarket_entropy.psi_news_blobs: concatenated per-country news content forpsychological_wavelengthjudgment.regions.<schema_key>: single-market region tilts (bypass coverage threshold per BUG-009).sectors.<sector>: cross-country weighted-median, coverage-gated.macro_regime: inferred growth/risk tokens from telemetry.regime_tag.fan_out_summary: market counts.
Step 5 — Component composition
Call pillar_compose.compute_pillars(aggregated, telemetry). Returns four PillarResult(value, confidence, missing_inputs, composition_formula, source_snippets) objects (module/class names kept as field identifiers for data-contract stability).
Component confidence caps (MCP_FIELD_INVENTORY.md §4):
econometrics_phase(macro backdrop): ≤ 0.80 (regime_tag + cross-country growth nudge).valuation_state(valuation): ≤ 0.70 (prose-extracted from per-country macro_indicators). PROSE-BASED — assumedtelemetry.signals.valuation_zdoes NOT exist (verified A0).market_entropy(market state): ≤ 0.50 (composite: prose entropy + normalizedlen(telemetry.divergences)as fallback proxy;composition_formuladocuments the weighting).psychological_wavelength(sentiment): ≤ 0.60. Default is heuristic bag-of-words; SKILL.md flow injects a Claude structured-output prompt viapsychological_judge_fnfor production.
When missing_inputs is non-empty, confidence is capped at 0.35 (strictly below gap_detect.LOW_CONFIDENCE_THRESHOLD = 0.4) so the gap branch fires.
psychological_wavelength judgment is Claude-only. Telemetry contains CG-proprietary signals — do NOT dispatch to external models. Structured-output schema:
{"value": -2..+2, "reasoning": "≤200 chars", "confidence": 0.0-1.0}
Step 6 — Optional gap-fill finishing pass
OPTIONAL. After Steps 4-5, any residually silent leaves (e.g., sectors where coverage was below threshold but a single anchor market had a strong signal) MAY be folded via gap_suggest.fold_responses. Step 4-5 results win on conflict. Skip in v0 — leave the hook for follow-up integration.
Step 7 — Shared confirmation gate
Build the GateContext and call gate_present.run_gate_loop(draft, context, dispose_fn, edit_fn):
GateContext(
source_label=f"Parallax synthesis {iso_timestamp}",
uploader_present=False,
confidence_map={
"pillars": mean([omega.confidence, phi.confidence, xi.confidence, psi.confidence]),
"sectors": <aggregated sector confidence>,
"regions": <aggregated region confidence>,
"factors": <factor confidence>, # 0.0 in v0 — maker doesn't populate factors
"macro_regime": <macro_regime confidence>,
},
extraction_attempt_action=True,
disposition_options=["confirm", "edit", "regenerate", "abandon"],
)
Bridge dispose_fn and edit_fn to AskUserQuestion exactly as /parallax-load-house-view Step 3 does — the module is shared.
Branch on GateResult.disposition:
confirm/edited→ Step 8 (save).regenerate→ re-run from Step 2 with the basis hint updated; return to Step 7.abandon→ no save, no auditgeneraterow. Done.
Step 8 — Save (reuses load-house-view Step 4 path)
Call maker.MakerOrchestrator.save_view(...). This:
-
Computes
view_hashper schema.yaml §"view_hash computation" (pinned algorithm). -
Builds
provenance.yamlwith onegenerator_synthesisentry per non-neutral leaf (perschema.yaml § classification_taxonomy.generator_synthesis). Validates viaprovenance_classes.validate_provenance_entry. -
Computes
provenance_hash = sha256(JCS(provenance_data)). -
Sets
extraction.uploader_confirmed = true,metadata.upload_timestamp,metadata.calibration_status = "heuristic_phase0". -
Renders
prose.mdviaprose_synth.render_prose(deterministic, no LLM). Computesprose_body_hash. -
Writes
view.yaml,prose.md(with frontmatterpaired_yaml_hash/prose_body_hash/view_id/version_id),provenance.yaml. chmod 0600. -
Appends
action: "generate"audit row viaaudit_chain.append_entry— fields per loader.md §6.1/§6.2:{ "schema_version": 1, "ts": "...", "view_id": "...", "version_id": "...", "view_hash": "...", "skill": "parallax-make-house-view", "action": "generate", "applied": true, "parent_version_id": null, "provenance_hash": "...", "source_tools": ["macro_analyst:United_States:macro_indicators", ...], "calibration_status": "heuristic_phase0" }FORBIDDEN on the audit row (per loader.md §6.2):
composition_formula,aggregator_weights_ref,source_snippets,pillar_missing_inputs. These live inprovenance.yaml. The maker raises if any forbidden key appears. -
Emits reasoning chain via
chain_emit.emit_phase_0_chain:skill_version="[email protected]"base_scores={"response_inline": <aggregated MCP responses>, "response_hash": <sha256 of sorted JCS concat>}final_portfolio={"weights": {}}(dummy — maker produces no portfolio)run_id=<new uuid>
Step 9 — Confirmation summary
✓ House view synthesized and saved.
view_name: Parallax synthesis YYYY-MM-DD
view_id: <uuid>
version_id: <uuid>
effective: <today> through <today + 30d>
components: econometrics_phase <e> valuation_state <v> market_entropy <m> psychological_wavelength <p>
component conf: econometrics_phase <c> valuation_state <c> market_entropy <c> psychological_wavelength <c> (avg <avg>)
tilts active: <count of non-zero tilts>
unreachable markets: <count>
Active in: portfolio-builder, rebalance, thematic-screen, morning-brief, client-review, explain-portfolio.
Conflict-flag only in: should-i-buy, deep-dive.
Try it:
/parallax-portfolio-builder "<your thesis>" # build a portfolio with this view applied
/parallax-judge-house-view # compare this synthesis against live signals later
Audit & inspect:
/parallax-load-house-view --status # active view summary
/parallax-load-house-view --why <tilt-path> # why is this tilt set to what it is?
/parallax-load-house-view --export <view_id> # regulator-grade compliance bundle
To clear: /parallax-load-house-view --clear
Shadow-diff mode
/parallax-make-house-view --shadow-diff runs Steps 1-6, then short-circuits Step 7/8. Instead of routing through the gate, it loads the currently-active bank view and renders an ADDITIVE diff via shadow_diff.render_shadow_diff. No save. The framing is critical — the bank's view is sovereign, the synthesis is offered as data to consider, never as a correction.
Compare mode
/parallax-make-house-view --compare <path_a> <path_b> loads two saved view bundles from disk (each arg may be a bundle directory or a view.yaml path) and renders a NEUTRAL per-cell diff of their tilts and excludes. It short-circuits before Step 1 — no MCP calls, no synthesis, no gate, no save, no audit row. Invoke maker.run_compare(Path(path_a), Path(path_b)) and render the returned string.
Unlike --shadow-diff (which synthesizes a Parallax view and frames it as additive against the sovereign active bank view), --compare treats both inputs as equally sovereign: symmetric left_only / right_only / agree / disagree buckets, no synthesis, and no pillar-confidence block (pillar tilt cells appear like any other cell, but the confidence/computation internals never do).
Disambiguation: --shadow-diff = synth-vs-active (synthesis runs); /parallax-judge-house-view = active-view-vs-live-signals; /parallax-house-view-diff = portfolio-OUTPUT diff (Leg A vs Leg B weights, not a tilt-cell diff).
Status mode
/parallax-make-house-view --status shows last-generated view metadata + the standard view_status.banner (same banner the loader emits). No MCP calls.
Modules
maker.py— orchestrator + save path.MakerOrchestrator.execute_synthesis(mcp, dispose_fn, edit_fn)is the entry point.cross_country.py—aggregate(per_market_responses, telemetry, weights)with 60% coverage rule.pillar_compose.py—compute_pillars(aggregated, telemetry)packaging.pillar_formulas.py—compute_omega / compute_phi / compute_xi / compute_psi(function names kept as field identifiers). Prose-basedvaluation_state/market_entropyper A0 findings. Confidence caps + missing-input rule.prose_synth.py— deterministic YAML → markdown narrative (no LLM).shadow_diff.py—--shadow-diffrendering (render_shadow_diff) +--comparerendering (render_compare/diff_excludes).maker.run_compare(left, right)is the--compareentry point.
Hard constraints
- DO NOT modify any existing skill (load-house-view, house-view-diff, stress-house-view).
- DO NOT modify any shared infra (
audit_chain.py,manifest_cache.py,chain_emit.py,view_status.py,gate_present.py,provenance_classes.py). Reuse only. - DO NOT dispatch to external models for
psychological_wavelength— Claude only (data perimeter). - The
generateaudit row format is fixed by loader.md §6.2 — adding fields requires a loader.md spec update first.