Install
$ agentstack add skill-bencharoenwong-parallax-workflows-parallax-make-house-view ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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 macroanalyst calls + 1 listmacrocountries + 1 gettelemetry. 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: schemaversion, ts, viewid, versionid, viewhash, skill, action, applied, parentversionid, provenancehash, sourcetools, calibrationstatus. compositionformula / aggregatorweightsref / sourcesnippets / pillarmissing_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 "" # 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 # 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 MCPFIELDINVENTORY.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.
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.: single-market region tilts (bypass coverage threshold per BUG-009).sectors.: 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 (MCPFIELDINVENTORY.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": ,
"regions": ,
"factors": , # 0.0 in v0 — maker doesn't populate factors
"macro_regime": ,
},
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:
``json { "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.
- Emits reasoning chain via
chain_emit.emit_phase_0_chain:
skill_version="parallax-make-house-view@1.0.0"base_scores={"response_inline": , "response_hash": }final_portfolio={"weights": {}}(dummy — maker produces no portfolio)run_id=
Step 9 — Confirmation summary
✓ House view synthesized and saved.
view_name: Parallax synthesis YYYY-MM-DD
view_id:
version_id:
effective: through
components: econometrics_phase valuation_state market_entropy psychological_wavelength
component conf: econometrics_phase valuation_state market_entropy psychological_wavelength (avg )
tilts active:
unreachable markets:
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 "" # 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 # why is this tilt set to what it is?
/parallax-load-house-view --export # 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 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.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: bencharoenwong
- Source: bencharoenwong/parallax-workflows
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.