suggested_startups ยท diff

git:20260902.af2a47c to git:20260906.f9cef19

62 added, 35 removed. Audit A to A.

---
name: suggested_startups
- description: Rank a provided list of startups against a list of investors by matching startup value propositions with investor professional backgrounds and interests.
+ description: Rank stored startup profiles for selected investors using their existing investor profiles. Use for personalized startup suggestions without discovery or profile generation.
---
- ## Skill Prompt: `suggested_startups`
+ # Suggested startups
- **Objective:** Rank a provided list of startups against a list of investors by matching startup value propositions with investor professional backgrounds and interests.
+ Match startup profiles to each investor's professional background and interests.
- **Inputs:**
+ ## Inputs and outputs
- * `dataset_name` (Optional, Default=`"sictic_members"`): Community dataset containing investors.
- * `startups` (Optional): Startup names to consider. If omitted, discover startup datasets dynamically.
- * `investors` (Optional): Investor names or LinkedIn IDs to process. If omitted, use all canonical persons with LinkedIn IDs from `persons_in_dataset`.
- * `max_startups` (Optional, Default=5): Maximum suggested startups per investor.
+ The async `suggested_startups(dataset_name="sictic_members", startups=None,
+ investors=None, max_startups=16)` returns one Markdown table per investor in
+ `list[InsightFile]`. Tables contain Startup, Dealum and Rationale columns,
+ ordered by rank.
- **Procedure:**
+ Pass `Person.identifier` to `InsightFile`. Eligible investors have LinkedIn IDs,
+ giving `suggested-startups/<linkedin-id>-<model>.md` within the community's
+ insights directory. Use display names only in report text. Investor-profile
+ inputs use the same LinkedIn identity.
- 1. **Input Resolution:**
- * Slugify `dataset_name`.
- * Resolve investors to canonical `Person` objects from `persons_in_dataset(dataset_slug)` and preserve their LinkedIn IDs throughout profile lookup.
- * If `startups` is empty, discover startup datasets with `list_dataset_names("startups")`, excluding configured community and ignored datasets from `load_repository_config("bulk_refresh")`.
+ ## Workflow and dependencies
- 2. **Configuration:**
- * Load the strategic-fit objective from `load_repository_config("suggested_startups")`.
- * Build one `config_cache_key()` from the complete suggested-startups, `ranking_top_k`, and `ranking_rationale` config sections plus the selected startups and `max_startups`.
- * Do not copy, parse, embed, or index investor or startup-profile insights.
+ Read the existing canonical roster with the synchronous
+ `lib.people.discovery.persons_in_dataset` reader. Resolve supplied investor
+ names or LinkedIn IDs to roster members. Omitted or empty investor selection
+ uses all eligible members; LinkedIn IDs are required.
- 3. **Per-Investor Output:**
- * For each investor, construct `lib.insights.InsightFile(dataset=dataset_slug, skill="suggested_startups", model=llm_model(), identifier=investor, subdir=True, config_key=suggested_config_key)`.
- * Recompute the ranking whenever the skill is explicitly invoked; ranking outputs do not participate in dataset-revision freshness caching.
+ Resolve supplied startup names to dataset slugs and remove duplicates. Omitted
+ or empty startup selection lists startup datasets, excluding configured
+ community and ignored datasets. This default does not filter by active status.
+ Read preferred stored `startup_profile` and `investor_profile` insights;
+ manual input profiles take precedence. Validate all required profiles before
+ making any ranking calls.
- 4. **Stored Profile Preparation:**
- * Select startup-profile insights with `select_insights(startups, "startup_profile")`; fail before any LLM call if a requested profile is missing or duplicated.
- * Compile those selected `InsightFile` objects into an ID-to-profile mapping without regenerating startup profiles.
- * Load each pending investor's stored investor profile by canonical LinkedIn ID; fail before any LLM call if any profile is missing. Do not refresh investor profiles as a side effect.
+ Insert each investor profile into the configured objective, then call
+ `ranking_top_k` and `ranking_rationale` with canonical startup dataset IDs.
+ Use the shared engine's default batch size. The prompt asks for previously
+ seen startups to rank last; strict exclusion is not enforced, so they may
+ appear when the requested limit exceeds the eligible candidates.
- 5. **Per-Investor Startup Selection:**
- * For each pending investor, rank every selected startup profile with `ranking_top_k`, using its default batch size, the stored investor profile as the objective context, and `max_startups` as `top_k`.
- * Generate balanced rationales for the finalists with `ranking_rationale`, preserving canonical startup IDs throughout.
+ Recompute on each invocation; no cached or manual suggestion output is selected.
+ The configuration key records the objective, shared ranking/structured-output
+ configuration, selected startup list and limit. Direct calls are read-only
+ for source profiles. Bulk refresh declares `startup-profile` and
+ `investor-profile` prerequisites; their execution follows registry scope.
- 6. **Output Generation:**
- * Save one Markdown table per investor with `insight.save(content)` only after all validation succeeds.
- * Catch generation, validation, and save errors per investor. Log the full error, do not save that investor's invalid output, and continue processing the remaining investors.
- * Log a final summary with generated and failed counts. Keep this operational summary out of the return value.
- * Log each `insight.path` and return all generated artifacts as
- a flat `list[InsightFile]`. Do not hardcode `<REPO_PATH>/insights/...`
- paths.
+ ## Side effects and failure behavior
+ Call models and save one report per investor through `InsightFile`.
+ No discovery, enrichment, source-profile refresh, ingestion or outreach occurs.
+ Dealum links use stored manifest metadata without external requests.
+
+ Unresolved requested investors, missing LinkedIn IDs in explicit selections,
+ an empty resolved request, or missing/duplicate startup profiles raise before
+ generation. Default investor selection skips people without LinkedIn IDs;
+ missing selected investor profiles raise.
+
+ Investors are processed concurrently. Generation or validation failures prevent
+ saving that investor's new report. Other investors continue and successful
+ reports remain saved; after processing all investors, any failure raises an
+ aggregate `RuntimeError` rather than returning a partial list. Save failures
+ are also included in that error.
+
## Usage
```bash
- conda run -n sictic-env python -m skills.harness /suggested_startups --startups "<startup1>,<startup2>" --investor "<name1>,<name2>"
+ conda run -n sictic-env python -m skills.harness '/suggested_startups --startups "<startup1>,<startup2>" --investor "<name-or-id1>,<name-or-id2>"'
```
+
+ The harness uses comma-separated startup and investor lists. The direct CLI uses
+ repeated `--startups` options and a comma-separated `--investor` value.
+ Both accept `--max-startups` (default 16); only the Python API exposes
+ `dataset_name`.
+
+ ## References
+
+ - [Orchestration](suggested_startups.py), [input selection](inputs.py)
+ - [Generation and rendering](generation.py)
+ - [Objective](../../config/suggested_startups/suggested_startups_prompt.md)
+ - [Shared ranking contracts](../ranking/SKILL.md)