|
| 1 | +Merge a single campaign's extracted knowledge into the cross-campaign registry. |
| 2 | + |
| 3 | +## Usage |
| 4 | + |
| 5 | +`/index-wiki <campaign-name>` — indexes one campaign into registry.json |
| 6 | + |
| 7 | +## Steps |
| 8 | + |
| 9 | +1. **Resolve campaign**: Use `$ARGUMENTS` as the campaign name. Verify `~/.nous/wiki/campaigns/<campaign-name>/concepts.json` exists. |
| 10 | + |
| 11 | +2. **Read per-campaign files**: From `~/.nous/wiki/campaigns/<campaign-name>/`: |
| 12 | + - `concepts.json` — contains `repo_path`, `system_name`, `research_question`, `campaign_name`, `date`, plus `entities`, `concepts`, `parameters` |
| 13 | + - `principles.json` — full principle definitions (extract IDs for the registry) |
| 14 | + - `dead-ends.json` — refuted approaches |
| 15 | + - `frontiers.json` — boundary conditions |
| 16 | + - `interactions.json` — untested combinations |
| 17 | + |
| 18 | +3. **Load existing registry**: Read `~/.nous/wiki/registry.json`. If it doesn't exist, initialize: |
| 19 | + ```json |
| 20 | + {"version": 1, "projects": {}} |
| 21 | + ``` |
| 22 | + |
| 23 | +4. **Idempotency check**: Look for this campaign in `projects[repo_path].campaigns[]` by name. If already present, report "Campaign already in registry — skipping" and stop. |
| 24 | + |
| 25 | +5. **Find or create project**: Look up `projects[repo_path]`. If not present, create: |
| 26 | + ```json |
| 27 | + { |
| 28 | + "name": "<system_name from concepts.json>", |
| 29 | + "campaigns": [], |
| 30 | + "entities": [] |
| 31 | + } |
| 32 | + ``` |
| 33 | + |
| 34 | +6. **Process entities**: For each entity in `concepts.json`: |
| 35 | + - Normalize the name (lowercase, strip content in parentheses) for matching |
| 36 | + - Check if an entity with the same normalized name exists in `projects[repo_path].entities[]` |
| 37 | + - If yes: add this campaign name to its `campaigns[]` array (if not already there) |
| 38 | + - If no: assign new ID `E-{max_entity_id + 1}`, create entry: |
| 39 | + ```json |
| 40 | + {"id": "E-N", "name": "<entity name>", "aliases": [], "campaigns": ["<campaign-name>"]} |
| 41 | + ``` |
| 42 | + - Track the max entity ID across all projects globally (IDs are globally unique) |
| 43 | + |
| 44 | +7. **Build campaign object**: Create the campaign entry for the registry: |
| 45 | + ```json |
| 46 | + { |
| 47 | + "name": "<campaign_name>", |
| 48 | + "date": "<date>", |
| 49 | + "research_question": "<research_question>", |
| 50 | + "concepts": [], |
| 51 | + "parameters": [], |
| 52 | + "principles": [], |
| 53 | + "dead_ends": [], |
| 54 | + "frontiers": [], |
| 55 | + "interactions": [] |
| 56 | + } |
| 57 | + ``` |
| 58 | + |
| 59 | + - **Concepts**: For each concept in `concepts.json`, assign ID `C-{max+1}` and add `{"id": "C-N", "name": "<concept name>"}` to the campaign's `concepts[]` |
| 60 | + - **Parameters**: For each parameter in `concepts.json`, assign ID `P-{max+1}` and add `{"id": "P-N", "name": "<parameter name>"}` to the campaign's `parameters[]` |
| 61 | + - **Principles**: Read `principles.json`, add each principle's ID string (e.g., "RP-1") to the campaign's `principles[]` array |
| 62 | + - **Dead-ends**: For each entry in `dead-ends.json`, assign ID `DE-{max+1}` and add `{"id": "DE-N", "title": "<title>"}` to the campaign's `dead_ends[]` |
| 63 | + - **Frontiers**: For each entry in `frontiers.json`, assign ID `F-{max+1}` and add `{"id": "F-N", "title": "<title>"}` to the campaign's `frontiers[]` |
| 64 | + - **Interactions**: For each entry in `interactions.json`, assign ID `I-{max+1}` and add `{"id": "I-N", "title": "<title>"}` to the campaign's `interactions[]` |
| 65 | + |
| 66 | +8. **Add campaign to project**: Append the campaign object to `projects[repo_path].campaigns[]`. |
| 67 | + |
| 68 | +9. **Recompute entity clusters**: Using ALL entities in `projects[repo_path].entities[]` and their definitions from per-campaign `concepts.json` files: |
| 69 | + |
| 70 | + a. For each entity, gather its definition from the campaign(s) that define it (read `~/.nous/wiki/campaigns/<campaign>/concepts.json` → `entities[]` → match by name). If an entity appears in multiple campaigns, concatenate definitions with ` | `. |
| 71 | + |
| 72 | + b. Semantically group entities by functional role/purpose into clusters. Rules: |
| 73 | + - Min 2 entities per cluster, max 10 |
| 74 | + - Max 20 clusters total per project — if grouping would exceed 20, merge the smallest/most-similar clusters |
| 75 | + - Each entity belongs to at most one cluster (singletons are valid — omit them) |
| 76 | + - Labels: 2-4 words, Title Case, describe the functional group |
| 77 | + |
| 78 | + c. Assign sequential IDs `EC-1`, `EC-2`, etc. Set `projects[repo_path].entity_clusters` to: |
| 79 | + ```json |
| 80 | + [{"id": "EC-1", "label": "...", "entities": ["E-4", "E-5", "E-6"]}, ...] |
| 81 | + ``` |
| 82 | + |
| 83 | + d. **CRITICAL**: This step ONLY writes the `entity_clusters` field. Do NOT modify `name`, `campaigns`, `entities`, or any other field on the project or registry. |
| 84 | + |
| 85 | +10. **Write registry.json**: Write the updated registry to `~/.nous/wiki/registry.json` with 2-space indentation. |
| 86 | + |
| 87 | +11. **Report**: Print the registry path and a summary of what was added (counts of entities, concepts, parameters, principles, dead-ends, frontiers, interactions). |
| 88 | + |
| 89 | +## ID Assignment Rules |
| 90 | + |
| 91 | +- IDs are **globally unique** across all projects and campaigns |
| 92 | +- To find the next ID for a type (e.g., "C-"), scan ALL projects and campaigns in the registry for the maximum existing ID of that type, then increment |
| 93 | +- Entity IDs (E-N) are unique per-project in practice but globally unique in assignment |
| 94 | +- Principle IDs (RP-N) are NOT reassigned — they keep their original campaign-local IDs |
| 95 | + |
| 96 | +## Deduplication Rules |
| 97 | + |
| 98 | +- **Entities**: Match by normalized name (lowercase, strip parenthetical suffixes). If matched, add campaign to existing entity's `campaigns[]` array. |
| 99 | +- **Everything else** (concepts, parameters, dead-ends, frontiers, interactions): Always create new entries. Different campaigns may have similar-sounding entries that are contextually distinct. Never deduplicate these across campaigns. |
| 100 | + |
| 101 | +## Important Rules |
| 102 | + |
| 103 | +- This skill only reads from `~/.nous/wiki/campaigns/` — it never reaches back to source repos |
| 104 | +- This is the ONLY skill that writes to `registry.json` |
| 105 | +- All metadata (`repo_path`, `system_name`, `research_question`) comes from the per-campaign `concepts.json`, which was populated by `/post-campaign` from the source `campaign.yaml` |
| 106 | +- Process one campaign at a time (read registry → modify → write) to prevent conflicts |
| 107 | +- `entity_clusters` is the ONLY registry field that is recomputed (not appended). It is fully replaced each time a new campaign is indexed. All other data is strictly append-only. |
0 commit comments