agentsclimarketplace

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

Install
npx -y skills add bencharoenwong/parallax-workflows --skill parallax-make-house-view

Assembled 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.md BEFORE assuming any component input is available. valuation_state / market_entropy are prose-extracted; psychological_wavelength is 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-diff skips the gate AND the save.
  • --compare is 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 generate audit 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 in provenance.yaml, NOT on the audit row.
  • psychological_wavelength judgment 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_SKILLS in 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_income is deferred — no v0 formula consumes it; re-add it in the same change that lands a rates leg in pillar_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-market valuation_state prose-extracted values when ≥ 60% of weight responded; else NULL + coverage_warning.
  • xi.value etc.: same shape for market_entropy.
  • psi_news_blobs: concatenated per-country news content for psychological_wavelength judgment.
  • 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 — assumed telemetry.signals.valuation_z does NOT exist (verified A0).
  • market_entropy (market state): ≤ 0.50 (composite: prose entropy + normalized len(telemetry.divergences) as fallback proxy; composition_formula documents the weighting).
  • psychological_wavelength (sentiment): ≤ 0.60. Default is heuristic bag-of-words; SKILL.md flow injects a Claude structured-output prompt via psychological_judge_fn for 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 audit generate row. Done.

Step 8 — Save (reuses load-house-view Step 4 path)

Call maker.MakerOrchestrator.save_view(...). This:

  1. Computes view_hash per schema.yaml §"view_hash computation" (pinned algorithm).

  2. Builds provenance.yaml with one generator_synthesis entry per non-neutral leaf (per schema.yaml § classification_taxonomy.generator_synthesis). Validates via provenance_classes.validate_provenance_entry.

  3. Computes provenance_hash = sha256(JCS(provenance_data)).

  4. Sets extraction.uploader_confirmed = true, metadata.upload_timestamp, metadata.calibration_status = "heuristic_phase0".

  5. Renders prose.md via prose_synth.render_prose (deterministic, no LLM). Computes prose_body_hash.

  6. Writes view.yaml, prose.md (with frontmatter paired_yaml_hash / prose_body_hash / view_id / version_id), provenance.yaml. chmod 0600.

  7. Appends action: "generate" audit row via audit_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 in provenance.yaml. The maker raises if any forbidden key appears.

  8. 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-based valuation_state / market_entropy per A0 findings. Confidence caps + missing-input rule.
  • prose_synth.py — deterministic YAML → markdown narrative (no LLM).
  • shadow_diff.py — --shadow-diff rendering (render_shadow_diff) + --compare rendering (render_compare / diff_excludes). maker.run_compare(left, right) is the --compare entry 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 generate audit row format is fixed by loader.md §6.2 — adding fields requires a loader.md spec update first.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.