| name | update-portfolio-targets |
| plugin | portfolio-advisor |
| description | Update target portfolio weights — by pillar (theme) and by holding (stock) — in the canonical thesis JSON file. Validates weights sum to 100% before writing. Bumps the thesis version and appends a changeLog entry. Aligns changes with the investment thesis document and strategic review proposals. Trigger phrases: "update target weights", "change target allocation", "apply formula changes", "update thesis targets", "adjust pillar weights", "set TICKER to X%", "rebalance the formula".
|
| allowed-tools | Bash, Read, Write |
Update Portfolio Targets Skill
Quick Reference
- Trigger: "update target weights", "apply formula changes from strategic review",
/update-portfolio-targets
- Thesis file:
investment_screener/backend/data/theses/target-portfolio.json (id: "target-portfolio")
- Thesis doc:
investment_screener/backend/data/theses/investment_thesis.md
- Canonical edit script:
plugins/portfolio-advisor/scripts/update_targets.py ← use this
- Legacy update script:
investment_screener/backend/py_services/update_thesis.py (older, per-holding patch style)
- ADR reference:
ADRs/ — cross-plugin script conventions
- Chains from:
strategic-review skill (after formula proposals are approved by user)
- Chains into:
rebalance-portfolio skill (to execute trades aligned with new targets)
Canonical Edit Commands
python3 plugins/portfolio-advisor/scripts/update_targets.py --set NVDA=6.5 META=4.5 CRWD=0 --write --blueprint
python3 plugins/portfolio-advisor/scripts/update_targets.py \
--add TICKER=1.5 --name "Company Name" --pillar compute --strategy sa-asi-race \
--note "Thesis rationale." --write --blueprint
python3 plugins/portfolio-advisor/scripts/update_targets.py --show
python3 plugins/portfolio-advisor/scripts/update_targets.py --set INTC=7.0 --dry-run
⚠️ Full Refresh Chain — Run After Every Target Change
--blueprint handles thesis.md automatically, but always complete the full chain:
python3 plugins/portfolio-advisor/scripts/update_targets.py --set ... --write --blueprint
python3 plugins/portfolio-advisor/scripts/update_targets.py \
--set GOOG=4.98 HUMN=2.86 KOID=2.69 ETHA=3.79 IBIT=2.60 COIN=3.11 CRCL=2.27 \
--write --blueprint
python3 plugins/portfolio-advisor/scripts/generate_review_json.py
python3 plugins/portfolio-advisor/scripts/verify_refresh.py
python3 investment_screener/backend/py_services/verify_thesis_sync.py
generate_portfolio_blueprint.py --write now refreshes all 3 table formats (6-col early sections, 7-col enriched, and Section IV). Never run blueprint separately — always use --blueprint flag on update_targets.py so targets and thesis.md stay atomic.
Context: What This File Controls
thesis.json is the single source of truth for:
- Pillar (theme) target weights — e.g. "AI Compute" = 43%, "Sovereign Finance" = 18%
- Individual holding target weights — e.g. INTC = 8%, NVDA = 15%
- Holding metadata — role (core/hedge/speculative/reserve), thesis-for-inclusion, thesis breakers
- Drift thresholds — when to alert vs when to rebalance
All health checks (/api/theses/target-portfolio/health), drift monitoring, conviction audits, and
rebalance calculations read from this file. Changes here flow immediately into all downstream analysis.
Step 0: Load Current State
python3 investment_screener/backend/py_services/update_thesis.py --list
Also read the investment thesis document for strategic context:
head -100 investment_screener/backend/data/theses/investment_thesis.md
Note: The filename investment_thesis.md is the canonical thesis doc.
As the thesis evolves, its content is updated in place — the filename remains stable.
Step 1: Establish the Proposed Changes
Changes may come from:
- A recent
strategic-review session (formula proposals in the review markdown)
- Direct user instruction ("set INTC to 8%")
- Research findings that shift conviction on a position
Confirm with the user before writing if the source is a strategic review proposal:
📋 Proposed formula changes from {review date}:
Pillars:
ai-compute: {old}% → {new}%
sovereign-finance: {old}% → {new}%
...
Holdings:
INTC: {old}% → {new}% (reason: reduced DCF conviction, maintain for SA 13F thesis)
AVGO: {old}% → {new}% (reason: increase to reflect networking moat)
...
Total pillar delta: {sum check} → target: 100%
Total holding delta: {sum check} → target: 100%
Shall I apply these changes?
Always wait for user confirmation before Step 2 when running from a proposal.
For direct "set X to Y%" instructions, proceed directly.
Step 2: Validate Before Writing (Dry Run)
For single-field changes:
python3 investment_screener/backend/py_services/update_thesis.py \
--holding INTC --target 8.0 \
--note "Strategic review 2026-05-02: reduce INTC weight, DCF SELL but SA thesis intact" \
--dry-run
For batch changes from a strategic review, write a patch file first:
cat > /tmp/formula_patch.json << 'EOF'
{
"pillars": [
{"id": "ai-compute", "targetWeight": 43.0},
{"id": "sovereign-finance", "targetWeight": 18.0}
],
"holdings": [
{"ticker": "INTC", "targetWeight": 8.0},
{"ticker": "AVGO", "targetWeight": 8.0},
{"ticker": "ZS", "targetWeight": 4.5},
{"ticker": "CRWD", "targetWeight": 0.5},
{"ticker": "CRWV", "targetWeight": 3.5}
]
}
EOF
python3 investment_screener/backend/py_services/update_thesis.py \
--patch /tmp/formula_patch.json \
--note "Strategic review 2026-05-02: formula v7.4 approved" \
--dry-run
Review the printed diff carefully. Check:
- Pillar weights sum to 100%
- Holding weights sum to 100%
- No unintended changes
Step 3: Apply the Changes
If dry run looks correct, remove --dry-run:
python3 investment_screener/backend/py_services/update_thesis.py \
--patch /tmp/formula_patch.json \
--note "Strategic review 2026-05-02: formula v7.4 approved"
The script will:
- Validate weight sums (exits with error if invalid)
- Increment
version in the JSON
- Append a
changeLog entry with date and note
- Atomically replace the file (write to
.tmp, then os.replace)
Step 4: Verify via API
API_TOKEN=$(cat .runtime/api-token)
curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:3001/api/theses/target-portfolio | python3 -c "
import json, sys
t = json.load(sys.stdin)
print(f'Name: {t[\"name\"]} version: {t[\"version\"]}')
print(f'Pillars: {len(t[\"pillars\"])} Holdings: {len(t[\"holdings\"])}')
for p in sorted(t['pillars'], key=lambda x: -x['targetWeight']):
print(f' {p[\"id\"]:<32} {p[\"targetWeight\"]:>6.2f}%')
"
curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:3001/api/theses/target-portfolio/health | python3 -c "
import json, sys
h = json.load(sys.stdin)
print(f'Health check at: {h[\"analyzedAt\"]}')
alerts = h.get('alerts', [])
critical = [a for a in alerts if a['severity'] == 'CRITICAL']
warnings = [a for a in alerts if a['severity'] == 'WARNING']
print(f'Alerts: {len(critical)} CRITICAL, {len(warnings)} WARNING')
for a in critical[:5]:
print(f' ⛔ {a[\"message\"]}')
for a in warnings[:5]:
print(f' ⚠️ {a[\"message\"]}')
"
Step 5: Update Thesis Document If Needed
If the formula change represents a meaningful strategic shift, update the thesis doc:
Step 6: Chain to Rebalance (Optional)
If the user wants to execute trades to restore alignment with the new targets:
Formula updated to v{N}. Current portfolio has the following drift against new targets:
{list of CRITICAL drift items from health check}
Would you like me to run /rebalance to generate the trade list?
Only chain to /rebalance if the user confirms.
Common Errors
| Error | Cause | Fix |
|---|
Pillar weights sum to X% (must be 100%) | Partial update — other pillars need offsetting adjustment | Adjust compensating pillars to balance |
Holding weights sum to X% | Same as above for holdings | Adjust other holdings |
pillar id 'X' not found | Typo in pillar id | Run --list to see valid ids |
ticker 'X' not found | Ticker not in thesis holdings | Add to holdings first (manual JSON edit) |
Adding a New Holding
New holdings must be added manually to thesis.json until an add-holding subcommand is built.
When adding:
- Choose the correct
pillarId (run --list to see ids)
- Set a realistic initial
targetWeight (reduce another holding proportionally)
- Add
thesisForInclusion — why does this stock belong in the active investment thesis portfolio?
- Optionally add 1-3
thesisBreakers — conditions that would force a full exit
- Validate with
--dry-run after editing the JSON directly
- Run
/evaluate-stock {TICKER} to generate the first AI valuation
Sources Checked Declaration
## Sources Checked
- thesis.json current state: [✅ Loaded v{N} / ❌ File missing]
- Thesis doc context: [✅ Reviewed / ⚠️ Skipped]
- Dry run validation: [✅ Passed / ❌ Failed — weights don't sum to 100%]
- Applied changes: [✅ Written v{N+1} / ❌ Skipped (dry run only)]
- API health check: [✅ Verified / ⚠️ Backend not running]
- Thesis synchronization: [✅ verify_thesis_sync.py passed / ❌ Failed/Out of sync]
- Thesis doc updated: [✅ Yes / ⚠️ No strategic shift, doc unchanged]