open-research-protocol 0.4.18 → 0.4.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ORP — Open Research Protocol
2
2
 
3
- Maintained by Fractal Research Group (`frg.earth`).
3
+ Maintained by SproutSeeds. Research stewardship: Fractal Research Group ([frg.earth](https://frg.earth)).
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/open-research-protocol?color=111111&label=npm)](https://www.npmjs.com/package/open-research-protocol)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/open-research-protocol?color=111111&label=downloads)](https://www.npmjs.com/package/open-research-protocol)
@@ -443,6 +443,11 @@ If you are using ORP normally, prefer:
443
443
  Reach for `orp pack ...` when you are doing advanced installs, ORP maintenance,
444
444
  or direct domain-template work.
445
445
 
446
+ Pack installation is pack-owned: `pack.yml` can describe installable
447
+ components, default includes, dependency checks, and report naming. That lets
448
+ ORP consume repo-owned external packs through `--pack-path` without baking
449
+ domain-specific install rules into ORP core.
450
+
446
451
  Install pack configs into a target repo (recommended):
447
452
 
448
453
  ```bash
@@ -39,6 +39,7 @@ Canonical fields:
39
39
  - `pack_id`: stable id
40
40
  - `name`, `version`, `description`
41
41
  - `orp_version_min`: optional compatibility floor
42
+ - `install`: optional install contract for `orp pack install`
42
43
  - `variables`: render-time variables (for example `TARGET_REPO_ROOT`)
43
44
  - `templates`: available config templates
44
45
 
@@ -80,6 +81,10 @@ orp pack fetch \
80
81
  --install-target /path/to/repo
81
82
  ```
82
83
 
84
+ Repo-owned local packs can also be installed directly with `--pack-path` as
85
+ long as their `pack.yml` includes an `install` block describing components,
86
+ default includes, and dependency-audit paths.
87
+
83
88
  This installs rendered config files and writes a dependency audit report:
84
89
 
85
90
  - `./orp.erdos-catalog-sync.yml`
@@ -218,6 +223,9 @@ orp erdos sync --problem-id 857 --problem-id 20
218
223
  - Packs can live in this repo (`packs/`) or external repos.
219
224
  - Users can copy/install packs without changing ORP core.
220
225
  - Version packs independently (for example `0.1.0`, `0.2.0`).
226
+ - Repo-owned packs can ship their own install metadata in `pack.yml`, so ORP
227
+ can install external domain packs without hardcoding those domains into ORP
228
+ core.
221
229
 
222
230
  ## Quality guidance
223
231
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research-protocol",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "description": "ORP CLI (Open Research Protocol): workspace ledgers, secrets, scheduling, governed execution, and agent-friendly research workflows.",
5
5
  "license": "MIT",
6
6
  "author": "Fractal Research Group <cody@frg.earth>",
@@ -129,3 +129,57 @@ templates:
129
129
  output_hint: orp.erdos-catalog-sync.yml
130
130
  default_profiles:
131
131
  - erdos_catalog_sync_active
132
+
133
+ install:
134
+ default_includes:
135
+ - catalog
136
+ - live_compare
137
+ - problem857
138
+ report_name: orp.erdos.pack-install-report.md
139
+ components:
140
+ catalog:
141
+ template_id: erdos_problems_catalog_sync
142
+ output_name: orp.erdos-catalog-sync.yml
143
+ description: Erdos catalog sync (all/open/closed/active snapshots).
144
+ live_compare:
145
+ template_id: sunflower_live_compare_suite
146
+ output_name: orp.erdos-live-compare.yml
147
+ description: Side-by-side atomic-board compare for Problems 857/20/367.
148
+ required_paths:
149
+ - analysis/problem857_counting_gateboard.json
150
+ - analysis/problem20_k3_gateboard.json
151
+ - analysis/problem367_sharp_gateboard.json
152
+ - scripts/problem857_ops_board.py
153
+ - scripts/problem20_ops_board.py
154
+ - scripts/problem367_ops_board.py
155
+ - scripts/frontier_status.py
156
+ problem857:
157
+ template_id: sunflower_problem857_discovery
158
+ output_name: orp.erdos-problem857.yml
159
+ description: Problem 857 discovery profile (board refresh/ready/spec/lean/frontier).
160
+ required_paths:
161
+ - analysis/problem857_counting_gateboard.json
162
+ - docs/PROBLEM857_COUNTING_OPS_BOARD.md
163
+ - orchestrator/v2/scopes/problem_857.yaml
164
+ - orchestrator/problem857_public_spec_check.py
165
+ - scripts/problem857_ops_board.py
166
+ - scripts/frontier_status.py
167
+ - sunflower_lean
168
+ governance:
169
+ template_id: sunflower_mathlib_pr_governance
170
+ output_name: orp.erdos-mathlib-pr-governance.yml
171
+ description: Mathlib PR governance profile set (pre-open, draft-readiness, full flow).
172
+ required_paths:
173
+ - docs/MATHLIB_SUBMISSION_CHECKLIST.md
174
+ - docs/MATHLIB_DRAFT_PR_TEMPLATE.md
175
+ - docs/MATHLIB_ISSUE_VIABILITY_GATE.md
176
+ - docs/UPSTREAM_PR_LANE.md
177
+ - analysis/UPSTREAM_PR_PLAN.yaml
178
+ - scripts/upstream-pr-plan.py
179
+ - scripts/upstream-pr-lane.sh
180
+ - scripts/mathlib-issue-viability-gate.py
181
+ - scripts/mathlib-naturality-snippet.sh
182
+ - scripts/mathlib-issue-local-gate.sh
183
+ - scripts/mathlib-tighten-fine-tooth-gate.sh
184
+ - scripts/mathlib-ready-to-draft-gate.sh
185
+ - scripts/mathlib-pr-body-preflight.py
@@ -144,3 +144,20 @@ templates:
144
144
  output_hint: orp.external-pr-feedback-hardening.yml
145
145
  default_profiles:
146
146
  - external_feedback_hardening
147
+
148
+ install:
149
+ default_includes:
150
+ - governance
151
+ - feedback_hardening
152
+ report_name: orp.external-pr.pack-install-report.md
153
+ components:
154
+ governance:
155
+ template_id: oss_pr_governance
156
+ output_name: orp.external-pr-governance.yml
157
+ description: Generic external contribution governance profiles (watch/select, pre-open, local-readiness, draft transition, draft lifecycle, full flow).
158
+ required_paths:
159
+ - analysis/PR_DRAFT_BODY.md
160
+ feedback_hardening:
161
+ template_id: oss_feedback_hardening
162
+ output_name: orp.external-pr-feedback-hardening.yml
163
+ description: Generic maintainer-feedback hardening profile.
@@ -176,3 +176,28 @@ templates:
176
176
  output_hint: orp.issue-smashers-feedback-hardening.yml
177
177
  default_profiles:
178
178
  - issue_smashers_feedback_hardening
179
+
180
+ install:
181
+ default_includes:
182
+ - workspace
183
+ - feedback_hardening
184
+ report_name: orp.issue-smashers.pack-install-report.md
185
+ components:
186
+ workspace:
187
+ template_id: issue_smashers_workspace
188
+ output_name: orp.issue-smashers.yml
189
+ description: Opinionated issue-smashers workspace and external contribution governance profiles.
190
+ required_paths:
191
+ - issue-smashers/README.md
192
+ - issue-smashers/WORKSPACE_RULES.md
193
+ - issue-smashers/setup-issue-smashers.sh
194
+ - issue-smashers/analysis/ISSUE_SMASHERS_WATCHLIST.json
195
+ - issue-smashers/analysis/ISSUE_SMASHERS_STATUS.md
196
+ - issue-smashers/analysis/PR_DRAFT_BODY.md
197
+ feedback_hardening:
198
+ template_id: issue_smashers_feedback_hardening
199
+ output_name: orp.issue-smashers-feedback-hardening.yml
200
+ description: Issue-smashers maintainer-feedback hardening profile.
201
+ required_paths:
202
+ - issue-smashers/WORKSPACE_RULES.md
203
+ - issue-smashers/analysis/ISSUE_SMASHERS_STATUS.md
@@ -24,7 +24,7 @@ from typing import Any
24
24
  import yaml
25
25
 
26
26
 
27
- PACK_SPECS: dict[str, dict[str, Any]] = {
27
+ LEGACY_PACK_SPECS: dict[str, dict[str, Any]] = {
28
28
  "erdos-open-problems": {
29
29
  "default_includes": ["catalog", "live_compare", "problem857"],
30
30
  "report_name": "orp.erdos.pack-install-report.md",
@@ -1267,18 +1267,99 @@ def _load_yaml(path: Path) -> dict[str, Any]:
1267
1267
  return payload
1268
1268
 
1269
1269
 
1270
- def _pack_spec(pack_id: str) -> dict[str, Any]:
1271
- spec = PACK_SPECS.get(pack_id)
1270
+ def _legacy_pack_spec(pack_id: str) -> dict[str, Any]:
1271
+ spec = LEGACY_PACK_SPECS.get(pack_id)
1272
1272
  if not isinstance(spec, dict):
1273
1273
  raise RuntimeError(f"unsupported pack for install flow: {pack_id}")
1274
1274
  return spec
1275
1275
 
1276
1276
 
1277
- def _pack_components(pack_id: str) -> dict[str, dict[str, Any]]:
1278
- components = _pack_spec(pack_id).get("components", {})
1277
+ def _pack_install_spec(pack_meta: dict[str, Any], pack_id: str) -> dict[str, Any]:
1278
+ install = pack_meta.get("install")
1279
+ if isinstance(install, dict):
1280
+ return install
1281
+ return _legacy_pack_spec(pack_id)
1282
+
1283
+
1284
+ def _pack_templates(pack_meta: dict[str, Any]) -> dict[str, dict[str, Any]]:
1285
+ raw = pack_meta.get("templates", {})
1286
+ if not isinstance(raw, dict):
1287
+ return {}
1288
+ out: dict[str, dict[str, Any]] = {}
1289
+ for key, value in raw.items():
1290
+ if isinstance(key, str) and isinstance(value, dict):
1291
+ out[key] = value
1292
+ return out
1293
+
1294
+
1295
+ def _normalize_install_component(
1296
+ component_key: str,
1297
+ raw_component: dict[str, Any],
1298
+ *,
1299
+ templates: dict[str, dict[str, Any]],
1300
+ ) -> dict[str, Any]:
1301
+ template_id = str(raw_component.get("template_id", "")).strip()
1302
+ if not template_id:
1303
+ raise RuntimeError(f"install component missing template_id: {component_key}")
1304
+
1305
+ template_meta = templates.get(template_id)
1306
+ if not isinstance(template_meta, dict):
1307
+ raise RuntimeError(
1308
+ f"install component {component_key} references unknown template_id: {template_id}"
1309
+ )
1310
+
1311
+ output_name = str(raw_component.get("output_name") or template_meta.get("output_hint") or "").strip()
1312
+ if not output_name:
1313
+ raise RuntimeError(
1314
+ f"install component {component_key} is missing output_name and template {template_id} has no output_hint"
1315
+ )
1316
+
1317
+ description = str(raw_component.get("description") or template_meta.get("description") or "").strip()
1318
+ required_paths_raw = raw_component.get("required_paths", [])
1319
+ if required_paths_raw is None:
1320
+ required_paths: list[str] = []
1321
+ elif isinstance(required_paths_raw, list):
1322
+ required_paths = [str(path) for path in required_paths_raw]
1323
+ else:
1324
+ raise RuntimeError(f"install component required_paths must be a list: {component_key}")
1325
+
1326
+ return {
1327
+ "template_id": template_id,
1328
+ "output_name": output_name,
1329
+ "description": description,
1330
+ "required_paths": required_paths,
1331
+ }
1332
+
1333
+
1334
+ def _pack_components(pack_meta: dict[str, Any], pack_id: str) -> dict[str, dict[str, Any]]:
1335
+ install = _pack_install_spec(pack_meta, pack_id)
1336
+ components = install.get("components", {})
1279
1337
  if not isinstance(components, dict) or not components:
1280
1338
  raise RuntimeError(f"pack has no installable components: {pack_id}")
1281
- return components
1339
+
1340
+ templates = _pack_templates(pack_meta)
1341
+ out: dict[str, dict[str, Any]] = {}
1342
+ for key, value in components.items():
1343
+ if not isinstance(key, str) or not isinstance(value, dict):
1344
+ raise RuntimeError(f"invalid install component entry in pack {pack_id}: {key!r}")
1345
+ out[key] = _normalize_install_component(key, value, templates=templates)
1346
+ return out
1347
+
1348
+
1349
+ def _pack_default_includes(pack_meta: dict[str, Any], pack_id: str) -> list[str]:
1350
+ install = _pack_install_spec(pack_meta, pack_id)
1351
+ raw = install.get("default_includes", [])
1352
+ if not isinstance(raw, list):
1353
+ return []
1354
+ return [str(x) for x in raw if isinstance(x, str)]
1355
+
1356
+
1357
+ def _pack_report_name(pack_meta: dict[str, Any], pack_id: str) -> str:
1358
+ install = _pack_install_spec(pack_meta, pack_id)
1359
+ raw = install.get("report_name")
1360
+ if isinstance(raw, str) and raw.strip():
1361
+ return raw.strip()
1362
+ return f"orp.{pack_id}.pack-install-report.md"
1282
1363
 
1283
1364
 
1284
1365
  def _validate_var(raw: str) -> str:
@@ -2153,15 +2234,13 @@ def main() -> int:
2153
2234
  pack_id = str(pack_meta.get("pack_id", args.pack_id))
2154
2235
  pack_version = str(pack_meta.get("version", "unknown"))
2155
2236
  generated_at_utc = _now_utc()
2156
- components = _pack_components(pack_id)
2237
+ components = _pack_components(pack_meta, pack_id)
2157
2238
  effective_vars = _vars_map(pack_meta, list(args.var or []))
2158
2239
  problem857_source_mode = _problem857_source_mode(effective_vars)
2159
2240
 
2160
2241
  includes = list(args.include or [])
2161
2242
  if not includes:
2162
- default_includes = _pack_spec(pack_id).get("default_includes", [])
2163
- if isinstance(default_includes, list):
2164
- includes = [str(x) for x in default_includes if isinstance(x, str)]
2243
+ includes = _pack_default_includes(pack_meta, pack_id)
2165
2244
  if not includes:
2166
2245
  includes = sorted(components.keys())
2167
2246
 
@@ -2227,7 +2306,7 @@ def main() -> int:
2227
2306
  else:
2228
2307
  report_path = report_path.resolve()
2229
2308
  else:
2230
- report_name = str(_pack_spec(pack_id).get("report_name", f"orp.{pack_id}.pack-install-report.md"))
2309
+ report_name = _pack_report_name(pack_meta, pack_id)
2231
2310
  report_path = (target_repo_root / report_name).resolve()
2232
2311
 
2233
2312
  _write_report(
@@ -34,6 +34,9 @@
34
34
  "orp_version_min": {
35
35
  "type": "string"
36
36
  },
37
+ "install": {
38
+ "$ref": "#/$defs/install"
39
+ },
37
40
  "variables": {
38
41
  "type": "object",
39
42
  "additionalProperties": {
@@ -90,6 +93,51 @@
90
93
  }
91
94
  }
92
95
  }
96
+ },
97
+ "install": {
98
+ "type": "object",
99
+ "additionalProperties": false,
100
+ "properties": {
101
+ "default_includes": {
102
+ "type": "array",
103
+ "items": {
104
+ "type": "string"
105
+ }
106
+ },
107
+ "report_name": {
108
+ "type": "string"
109
+ },
110
+ "components": {
111
+ "type": "object",
112
+ "additionalProperties": {
113
+ "$ref": "#/$defs/installComponent"
114
+ }
115
+ }
116
+ }
117
+ },
118
+ "installComponent": {
119
+ "type": "object",
120
+ "additionalProperties": false,
121
+ "required": [
122
+ "template_id"
123
+ ],
124
+ "properties": {
125
+ "template_id": {
126
+ "type": "string"
127
+ },
128
+ "output_name": {
129
+ "type": "string"
130
+ },
131
+ "description": {
132
+ "type": "string"
133
+ },
134
+ "required_paths": {
135
+ "type": "array",
136
+ "items": {
137
+ "type": "string"
138
+ }
139
+ }
140
+ }
93
141
  }
94
142
  }
95
143
  }