open-research-protocol 0.4.23 → 0.4.25
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/CHANGELOG.md +426 -0
- package/README.md +17 -0
- package/cli/orp.py +909 -4
- package/docs/AGENT_RUNTIME_BORROWING_NOTES.md +68 -0
- package/package.json +2 -1
- package/packages/orp-workspace-launcher/src/index.js +3 -0
- package/packages/orp-workspace-launcher/src/ledger.js +192 -33
- package/packages/orp-workspace-launcher/src/orp.js +61 -1
- package/packages/orp-workspace-launcher/src/tabs.js +147 -4
- package/packages/orp-workspace-launcher/test/ledger.test.js +226 -0
- package/packages/orp-workspace-launcher/test/tabs.test.js +60 -0
package/cli/orp.py
CHANGED
|
@@ -134,6 +134,9 @@ DEFAULT_HOSTED_BASE_URL = "https://orp.earth"
|
|
|
134
134
|
KERNEL_SCHEMA_VERSION = "1.0.0"
|
|
135
135
|
FRONTIER_SCHEMA_VERSION = "1.0.0"
|
|
136
136
|
FRONTIER_BANDS = ("exact", "structured", "horizon")
|
|
137
|
+
FRONTIER_ACTIVE_STATUSES = {"active", "in_progress", "running"}
|
|
138
|
+
FRONTIER_PENDING_STATUSES = {"", "pending", "planned", "ready"}
|
|
139
|
+
FRONTIER_TERMINAL_STATUSES = {"complete", "completed", "done", "skipped", "terminal"}
|
|
137
140
|
YOUTUBE_SOURCE_SCHEMA_VERSION = "1.0.0"
|
|
138
141
|
EXCHANGE_REPORT_SCHEMA_VERSION = "1.0.0"
|
|
139
142
|
MAINTENANCE_STATE_SCHEMA_VERSION = "1.0.0"
|
|
@@ -5869,10 +5872,12 @@ def _frontier_paths(repo_root: Path) -> dict[str, Path]:
|
|
|
5869
5872
|
"roadmap_json": root / "roadmap.json",
|
|
5870
5873
|
"checklist_json": root / "checklist.json",
|
|
5871
5874
|
"stack_json": root / "version-stack.json",
|
|
5875
|
+
"additional_json": root / "additional-items.json",
|
|
5872
5876
|
"state_md": root / "STATE.md",
|
|
5873
5877
|
"roadmap_md": root / "ROADMAP.md",
|
|
5874
5878
|
"checklist_md": root / "CHECKLIST.md",
|
|
5875
5879
|
"stack_md": root / "VERSION_STACK.md",
|
|
5880
|
+
"additional_md": root / "ADDITIONAL_ITEMS.md",
|
|
5876
5881
|
}
|
|
5877
5882
|
|
|
5878
5883
|
|
|
@@ -5923,6 +5928,18 @@ def _default_frontier_stack_payload(program_id: str, label: str) -> dict[str, An
|
|
|
5923
5928
|
}
|
|
5924
5929
|
|
|
5925
5930
|
|
|
5931
|
+
def _default_frontier_additional_payload(program_id: str = "", label: str = "") -> dict[str, Any]:
|
|
5932
|
+
return {
|
|
5933
|
+
"schema_version": FRONTIER_SCHEMA_VERSION,
|
|
5934
|
+
"kind": "orp_frontier_additional_items",
|
|
5935
|
+
"program_id": str(program_id).strip(),
|
|
5936
|
+
"label": str(label).strip(),
|
|
5937
|
+
"active_list_id": "",
|
|
5938
|
+
"active_item_id": "",
|
|
5939
|
+
"lists": [],
|
|
5940
|
+
}
|
|
5941
|
+
|
|
5942
|
+
|
|
5926
5943
|
def _frontier_load_stack(repo_root: Path) -> dict[str, Any]:
|
|
5927
5944
|
payload = _read_json_if_exists(_frontier_paths(repo_root)["stack_json"])
|
|
5928
5945
|
if not payload:
|
|
@@ -5976,6 +5993,485 @@ def _frontier_find_phase(
|
|
|
5976
5993
|
return None
|
|
5977
5994
|
|
|
5978
5995
|
|
|
5996
|
+
def _frontier_load_additional(repo_root: Path, stack: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
5997
|
+
payload = _read_json_if_exists(_frontier_paths(repo_root)["additional_json"])
|
|
5998
|
+
if not payload:
|
|
5999
|
+
stack_payload = stack if isinstance(stack, dict) else _read_json_if_exists(_frontier_paths(repo_root)["stack_json"])
|
|
6000
|
+
return _default_frontier_additional_payload(
|
|
6001
|
+
str((stack_payload or {}).get("program_id", "")).strip(),
|
|
6002
|
+
str((stack_payload or {}).get("label", "")).strip(),
|
|
6003
|
+
)
|
|
6004
|
+
if not isinstance(payload.get("lists"), list):
|
|
6005
|
+
payload["lists"] = []
|
|
6006
|
+
payload["active_list_id"] = str(payload.get("active_list_id", "")).strip()
|
|
6007
|
+
payload["active_item_id"] = str(payload.get("active_item_id", "")).strip()
|
|
6008
|
+
return payload
|
|
6009
|
+
|
|
6010
|
+
|
|
6011
|
+
def _frontier_find_additional_list(payload: dict[str, Any], list_id: str) -> dict[str, Any] | None:
|
|
6012
|
+
lists = payload.get("lists")
|
|
6013
|
+
if not isinstance(lists, list):
|
|
6014
|
+
return None
|
|
6015
|
+
for row in lists:
|
|
6016
|
+
if isinstance(row, dict) and str(row.get("id", "")).strip() == list_id:
|
|
6017
|
+
return row
|
|
6018
|
+
return None
|
|
6019
|
+
|
|
6020
|
+
|
|
6021
|
+
def _frontier_find_additional_item(item_list: dict[str, Any], item_id: str) -> dict[str, Any] | None:
|
|
6022
|
+
items = item_list.get("items")
|
|
6023
|
+
if not isinstance(items, list):
|
|
6024
|
+
return None
|
|
6025
|
+
for item in items:
|
|
6026
|
+
if isinstance(item, dict) and str(item.get("id", "")).strip() == item_id:
|
|
6027
|
+
return item
|
|
6028
|
+
return None
|
|
6029
|
+
|
|
6030
|
+
|
|
6031
|
+
def _frontier_active_additional_item(payload: dict[str, Any]) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
|
|
6032
|
+
list_id = str(payload.get("active_list_id", "")).strip()
|
|
6033
|
+
item_id = str(payload.get("active_item_id", "")).strip()
|
|
6034
|
+
if not list_id or not item_id:
|
|
6035
|
+
return (None, None)
|
|
6036
|
+
item_list = _frontier_find_additional_list(payload, list_id)
|
|
6037
|
+
if not isinstance(item_list, dict):
|
|
6038
|
+
return (None, None)
|
|
6039
|
+
item = _frontier_find_additional_item(item_list, item_id)
|
|
6040
|
+
if not isinstance(item, dict):
|
|
6041
|
+
return (None, None)
|
|
6042
|
+
return (item_list, item)
|
|
6043
|
+
|
|
6044
|
+
|
|
6045
|
+
def _frontier_normalize_additional_statuses(payload: dict[str, Any]) -> None:
|
|
6046
|
+
lists = payload.get("lists")
|
|
6047
|
+
if not isinstance(lists, list):
|
|
6048
|
+
payload["lists"] = []
|
|
6049
|
+
return
|
|
6050
|
+
for item_list in lists:
|
|
6051
|
+
if not isinstance(item_list, dict):
|
|
6052
|
+
continue
|
|
6053
|
+
items = item_list.get("items")
|
|
6054
|
+
if not isinstance(items, list):
|
|
6055
|
+
item_list["items"] = []
|
|
6056
|
+
items = item_list["items"]
|
|
6057
|
+
for item in items:
|
|
6058
|
+
if isinstance(item, dict):
|
|
6059
|
+
item["status"] = str(item.get("status", "")).strip() or "pending"
|
|
6060
|
+
if items and all(str(item.get("status", "")).strip() in {"complete", "skipped"} for item in items if isinstance(item, dict)):
|
|
6061
|
+
item_list["status"] = "complete"
|
|
6062
|
+
else:
|
|
6063
|
+
item_list["status"] = str(item_list.get("status", "")).strip() or "pending"
|
|
6064
|
+
|
|
6065
|
+
|
|
6066
|
+
def _frontier_next_pending_additional_item(payload: dict[str, Any]) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
|
|
6067
|
+
_frontier_normalize_additional_statuses(payload)
|
|
6068
|
+
lists = payload.get("lists")
|
|
6069
|
+
if not isinstance(lists, list):
|
|
6070
|
+
return (None, None)
|
|
6071
|
+
for item_list in lists:
|
|
6072
|
+
if not isinstance(item_list, dict) or str(item_list.get("status", "")).strip() in {"complete", "skipped"}:
|
|
6073
|
+
continue
|
|
6074
|
+
items = item_list.get("items")
|
|
6075
|
+
if not isinstance(items, list):
|
|
6076
|
+
continue
|
|
6077
|
+
for item in items:
|
|
6078
|
+
if isinstance(item, dict) and str(item.get("status", "")).strip() in {"", "pending"}:
|
|
6079
|
+
return (item_list, item)
|
|
6080
|
+
return (None, None)
|
|
6081
|
+
|
|
6082
|
+
|
|
6083
|
+
def _frontier_status(raw: Any, *, default: str = "pending") -> str:
|
|
6084
|
+
text = str(raw or "").strip().lower().replace(" ", "_").replace("-", "_")
|
|
6085
|
+
return text or default
|
|
6086
|
+
|
|
6087
|
+
|
|
6088
|
+
def _frontier_status_is_active(raw: Any) -> bool:
|
|
6089
|
+
return _frontier_status(raw, default="") in FRONTIER_ACTIVE_STATUSES
|
|
6090
|
+
|
|
6091
|
+
|
|
6092
|
+
def _frontier_status_is_pending(raw: Any) -> bool:
|
|
6093
|
+
return _frontier_status(raw, default="") in FRONTIER_PENDING_STATUSES
|
|
6094
|
+
|
|
6095
|
+
|
|
6096
|
+
def _frontier_status_is_terminal(raw: Any) -> bool:
|
|
6097
|
+
return _frontier_status(raw, default="") in FRONTIER_TERMINAL_STATUSES
|
|
6098
|
+
|
|
6099
|
+
|
|
6100
|
+
def _frontier_diagnostic_ok(issues: list[dict[str, Any]], *, strict: bool = False) -> bool:
|
|
6101
|
+
if any(str(issue.get("severity", "")).strip() == "error" for issue in issues):
|
|
6102
|
+
return False
|
|
6103
|
+
if strict and any(str(issue.get("severity", "")).strip() == "warning" for issue in issues):
|
|
6104
|
+
return False
|
|
6105
|
+
return True
|
|
6106
|
+
|
|
6107
|
+
|
|
6108
|
+
def _frontier_stack_summary(stack: dict[str, Any] | None) -> dict[str, int]:
|
|
6109
|
+
summary = {
|
|
6110
|
+
"versions": 0,
|
|
6111
|
+
"milestones": 0,
|
|
6112
|
+
"phases": 0,
|
|
6113
|
+
}
|
|
6114
|
+
if not isinstance(stack, dict):
|
|
6115
|
+
return summary
|
|
6116
|
+
versions = stack.get("versions")
|
|
6117
|
+
if not isinstance(versions, list):
|
|
6118
|
+
return summary
|
|
6119
|
+
for version in versions:
|
|
6120
|
+
if not isinstance(version, dict):
|
|
6121
|
+
continue
|
|
6122
|
+
summary["versions"] += 1
|
|
6123
|
+
milestones = version.get("milestones")
|
|
6124
|
+
if not isinstance(milestones, list):
|
|
6125
|
+
continue
|
|
6126
|
+
for milestone in milestones:
|
|
6127
|
+
if not isinstance(milestone, dict):
|
|
6128
|
+
continue
|
|
6129
|
+
summary["milestones"] += 1
|
|
6130
|
+
phases = milestone.get("phases")
|
|
6131
|
+
if isinstance(phases, list):
|
|
6132
|
+
summary["phases"] += len([phase for phase in phases if isinstance(phase, dict)])
|
|
6133
|
+
return summary
|
|
6134
|
+
|
|
6135
|
+
|
|
6136
|
+
def _frontier_compact_additional_item(item_list: dict[str, Any], item: dict[str, Any]) -> dict[str, str]:
|
|
6137
|
+
return {
|
|
6138
|
+
"list_id": str(item_list.get("id", "")).strip(),
|
|
6139
|
+
"list_label": str(item_list.get("label", "")).strip(),
|
|
6140
|
+
"item_id": str(item.get("id", "")).strip(),
|
|
6141
|
+
"item_label": str(item.get("label", "")).strip(),
|
|
6142
|
+
"status": _frontier_status(item.get("status")),
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
|
|
6146
|
+
def _frontier_additional_summary(payload: dict[str, Any]) -> dict[str, Any]:
|
|
6147
|
+
active_list_id = str(payload.get("active_list_id", "")).strip()
|
|
6148
|
+
active_item_id = str(payload.get("active_item_id", "")).strip()
|
|
6149
|
+
summary: dict[str, Any] = {
|
|
6150
|
+
"lists": 0,
|
|
6151
|
+
"items": 0,
|
|
6152
|
+
"pending_items": 0,
|
|
6153
|
+
"active_items": 0,
|
|
6154
|
+
"complete_items": 0,
|
|
6155
|
+
"skipped_items": 0,
|
|
6156
|
+
"open_items": 0,
|
|
6157
|
+
"active_list_id": active_list_id,
|
|
6158
|
+
"active_item_id": active_item_id,
|
|
6159
|
+
"active_pointer_partial": bool(active_list_id) != bool(active_item_id),
|
|
6160
|
+
"active_pointer_valid": False,
|
|
6161
|
+
"active_item_status": "",
|
|
6162
|
+
"next_pending": None,
|
|
6163
|
+
}
|
|
6164
|
+
lists = payload.get("lists")
|
|
6165
|
+
if not isinstance(lists, list):
|
|
6166
|
+
return summary
|
|
6167
|
+
summary["lists"] = len([row for row in lists if isinstance(row, dict)])
|
|
6168
|
+
for item_list in lists:
|
|
6169
|
+
if not isinstance(item_list, dict):
|
|
6170
|
+
continue
|
|
6171
|
+
items = item_list.get("items")
|
|
6172
|
+
if not isinstance(items, list):
|
|
6173
|
+
continue
|
|
6174
|
+
for item in items:
|
|
6175
|
+
if not isinstance(item, dict):
|
|
6176
|
+
continue
|
|
6177
|
+
status = _frontier_status(item.get("status"))
|
|
6178
|
+
summary["items"] += 1
|
|
6179
|
+
if _frontier_status_is_active(status):
|
|
6180
|
+
summary["active_items"] += 1
|
|
6181
|
+
summary["open_items"] += 1
|
|
6182
|
+
elif status == "skipped":
|
|
6183
|
+
summary["skipped_items"] += 1
|
|
6184
|
+
elif _frontier_status_is_terminal(status):
|
|
6185
|
+
summary["complete_items"] += 1
|
|
6186
|
+
else:
|
|
6187
|
+
summary["pending_items"] += 1
|
|
6188
|
+
summary["open_items"] += 1
|
|
6189
|
+
if summary["next_pending"] is None:
|
|
6190
|
+
summary["next_pending"] = _frontier_compact_additional_item(item_list, item)
|
|
6191
|
+
|
|
6192
|
+
if (
|
|
6193
|
+
active_list_id
|
|
6194
|
+
and active_item_id
|
|
6195
|
+
and str(item_list.get("id", "")).strip() == active_list_id
|
|
6196
|
+
and str(item.get("id", "")).strip() == active_item_id
|
|
6197
|
+
):
|
|
6198
|
+
summary["active_pointer_valid"] = True
|
|
6199
|
+
summary["active_item_status"] = status
|
|
6200
|
+
|
|
6201
|
+
return summary
|
|
6202
|
+
|
|
6203
|
+
|
|
6204
|
+
def _frontier_terminal_declared(state: dict[str, Any] | None, stack: dict[str, Any] | None) -> bool:
|
|
6205
|
+
state_obj = state if isinstance(state, dict) else {}
|
|
6206
|
+
stack_obj = stack if isinstance(stack, dict) else {}
|
|
6207
|
+
if bool(state_obj.get("terminal")) or bool(stack_obj.get("terminal")):
|
|
6208
|
+
return True
|
|
6209
|
+
completion_status = _frontier_status(
|
|
6210
|
+
state_obj.get("completion_status", state_obj.get("completionStatus", stack_obj.get("completion_status", ""))),
|
|
6211
|
+
default="",
|
|
6212
|
+
)
|
|
6213
|
+
return completion_status in {"complete", "completed", "done", "terminal"}
|
|
6214
|
+
|
|
6215
|
+
|
|
6216
|
+
def _frontier_build_continuation_payload(
|
|
6217
|
+
repo_root: Path,
|
|
6218
|
+
stack: dict[str, Any] | None,
|
|
6219
|
+
state: dict[str, Any] | None,
|
|
6220
|
+
*,
|
|
6221
|
+
include_structural_issues: bool = True,
|
|
6222
|
+
strict: bool = False,
|
|
6223
|
+
) -> dict[str, Any]:
|
|
6224
|
+
issues: list[dict[str, Any]] = []
|
|
6225
|
+
paths = _frontier_paths(repo_root)
|
|
6226
|
+
stack_obj = stack if isinstance(stack, dict) else None
|
|
6227
|
+
state_obj = state if isinstance(state, dict) else None
|
|
6228
|
+
|
|
6229
|
+
if stack_obj is None and include_structural_issues:
|
|
6230
|
+
issues.append({"severity": "error", "code": "missing_stack", "message": "frontier version stack is missing."})
|
|
6231
|
+
if state_obj is None and include_structural_issues:
|
|
6232
|
+
issues.append({"severity": "error", "code": "missing_state", "message": "frontier state is missing."})
|
|
6233
|
+
|
|
6234
|
+
additional = _frontier_load_additional(repo_root, stack_obj)
|
|
6235
|
+
additional_summary = _frontier_additional_summary(additional)
|
|
6236
|
+
stack_summary = _frontier_stack_summary(stack_obj)
|
|
6237
|
+
blockers = _coerce_string_list(state_obj.get("blocked_by") if isinstance(state_obj, dict) else [])
|
|
6238
|
+
terminal_declared = _frontier_terminal_declared(state_obj, stack_obj)
|
|
6239
|
+
active_primary = False
|
|
6240
|
+
active_primary_status = ""
|
|
6241
|
+
active_primary_kind = ""
|
|
6242
|
+
active_primary_id = ""
|
|
6243
|
+
next_action = str(state_obj.get("next_action", "")).strip() if isinstance(state_obj, dict) else ""
|
|
6244
|
+
suggested_next_command = ""
|
|
6245
|
+
|
|
6246
|
+
if stack_obj is not None and state_obj is not None:
|
|
6247
|
+
active_version = str(state_obj.get("active_version", "")).strip()
|
|
6248
|
+
active_milestone = str(state_obj.get("active_milestone", "")).strip()
|
|
6249
|
+
active_phase = str(state_obj.get("active_phase", "")).strip()
|
|
6250
|
+
version = _frontier_find_version(stack_obj, active_version) if active_version else None
|
|
6251
|
+
_, milestone = _frontier_find_milestone(stack_obj, active_milestone) if active_milestone else (None, None)
|
|
6252
|
+
phase = _frontier_find_phase(milestone, active_phase) if active_phase and isinstance(milestone, dict) else None
|
|
6253
|
+
|
|
6254
|
+
if isinstance(version, dict):
|
|
6255
|
+
version_status = _frontier_status(version.get("status", ""))
|
|
6256
|
+
if _frontier_status_is_terminal(version_status):
|
|
6257
|
+
issues.append(
|
|
6258
|
+
{
|
|
6259
|
+
"severity": "error",
|
|
6260
|
+
"code": "stale_active_version_complete",
|
|
6261
|
+
"message": f"active version `{active_version}` is marked `{version_status}`; advance the frontier or declare terminal completion.",
|
|
6262
|
+
}
|
|
6263
|
+
)
|
|
6264
|
+
elif active_version:
|
|
6265
|
+
active_primary = True
|
|
6266
|
+
active_primary_kind = "version"
|
|
6267
|
+
active_primary_id = active_version
|
|
6268
|
+
active_primary_status = version_status
|
|
6269
|
+
|
|
6270
|
+
if isinstance(milestone, dict):
|
|
6271
|
+
milestone_status = _frontier_status(milestone.get("status", ""))
|
|
6272
|
+
if _frontier_status_is_terminal(milestone_status):
|
|
6273
|
+
issues.append(
|
|
6274
|
+
{
|
|
6275
|
+
"severity": "error",
|
|
6276
|
+
"code": "stale_active_milestone_complete",
|
|
6277
|
+
"message": f"active milestone `{active_milestone}` is marked `{milestone_status}`; advance the frontier or declare terminal completion.",
|
|
6278
|
+
}
|
|
6279
|
+
)
|
|
6280
|
+
if active_primary_kind == "version":
|
|
6281
|
+
active_primary = False
|
|
6282
|
+
elif active_milestone:
|
|
6283
|
+
active_primary = True
|
|
6284
|
+
active_primary_kind = "milestone"
|
|
6285
|
+
active_primary_id = active_milestone
|
|
6286
|
+
active_primary_status = milestone_status
|
|
6287
|
+
|
|
6288
|
+
if isinstance(phase, dict):
|
|
6289
|
+
phase_status = _frontier_status(phase.get("status", ""))
|
|
6290
|
+
if _frontier_status_is_terminal(phase_status):
|
|
6291
|
+
issues.append(
|
|
6292
|
+
{
|
|
6293
|
+
"severity": "error",
|
|
6294
|
+
"code": "stale_active_phase_complete",
|
|
6295
|
+
"message": f"active phase `{active_phase}` is marked `{phase_status}`; record the handoff and move the frontier to the next phase or queue item.",
|
|
6296
|
+
}
|
|
6297
|
+
)
|
|
6298
|
+
active_primary = False
|
|
6299
|
+
elif active_phase:
|
|
6300
|
+
active_primary = True
|
|
6301
|
+
active_primary_kind = "phase"
|
|
6302
|
+
active_primary_id = active_phase
|
|
6303
|
+
active_primary_status = phase_status
|
|
6304
|
+
|
|
6305
|
+
if additional_summary["active_pointer_partial"]:
|
|
6306
|
+
issues.append(
|
|
6307
|
+
{
|
|
6308
|
+
"severity": "error",
|
|
6309
|
+
"code": "partial_active_additional_pointer",
|
|
6310
|
+
"message": "frontier additional queue has only one of active_list_id or active_item_id set.",
|
|
6311
|
+
}
|
|
6312
|
+
)
|
|
6313
|
+
|
|
6314
|
+
active_list_id = str(additional_summary["active_list_id"]).strip()
|
|
6315
|
+
active_item_id = str(additional_summary["active_item_id"]).strip()
|
|
6316
|
+
if active_list_id or active_item_id:
|
|
6317
|
+
active_list, active_item = _frontier_active_additional_item(additional)
|
|
6318
|
+
if active_list is None:
|
|
6319
|
+
issues.append(
|
|
6320
|
+
{"severity": "error", "code": "missing_active_additional_list", "message": f"active additional list `{active_list_id}` does not exist."}
|
|
6321
|
+
)
|
|
6322
|
+
elif active_item is None:
|
|
6323
|
+
issues.append(
|
|
6324
|
+
{
|
|
6325
|
+
"severity": "error",
|
|
6326
|
+
"code": "missing_active_additional_item",
|
|
6327
|
+
"message": f"active additional item `{active_item_id}` does not exist in list `{active_list_id}`.",
|
|
6328
|
+
}
|
|
6329
|
+
)
|
|
6330
|
+
else:
|
|
6331
|
+
active_status = _frontier_status(active_item.get("status", ""))
|
|
6332
|
+
if _frontier_status_is_terminal(active_status):
|
|
6333
|
+
issues.append(
|
|
6334
|
+
{
|
|
6335
|
+
"severity": "error",
|
|
6336
|
+
"code": "stale_active_additional_item",
|
|
6337
|
+
"message": f"active additional item `{active_list_id}/{active_item_id}` is marked `{active_status}`; complete the handoff and activate the next pending item.",
|
|
6338
|
+
}
|
|
6339
|
+
)
|
|
6340
|
+
elif not _frontier_status_is_active(active_status):
|
|
6341
|
+
issues.append(
|
|
6342
|
+
{
|
|
6343
|
+
"severity": "warning",
|
|
6344
|
+
"code": "active_additional_item_not_marked_active",
|
|
6345
|
+
"message": f"active additional item `{active_list_id}/{active_item_id}` is marked `{active_status}` instead of active.",
|
|
6346
|
+
}
|
|
6347
|
+
)
|
|
6348
|
+
if not next_action:
|
|
6349
|
+
next_action = _frontier_additional_item_summary(active_list, active_item)
|
|
6350
|
+
|
|
6351
|
+
if not active_item_id and int(additional_summary["pending_items"]) > 0:
|
|
6352
|
+
issues.append(
|
|
6353
|
+
{
|
|
6354
|
+
"severity": "warning",
|
|
6355
|
+
"code": "pending_additional_without_active_pointer",
|
|
6356
|
+
"message": "frontier additional queue has pending work but no active item; run `orp frontier additional activate-next` before delegating queue work.",
|
|
6357
|
+
}
|
|
6358
|
+
)
|
|
6359
|
+
suggested_next_command = "orp frontier additional activate-next --json"
|
|
6360
|
+
next_pending = additional_summary.get("next_pending")
|
|
6361
|
+
if isinstance(next_pending, dict) and not next_action:
|
|
6362
|
+
next_action = (
|
|
6363
|
+
f"Activate additional item {next_pending.get('list_id', '')}/{next_pending.get('item_id', '')}: "
|
|
6364
|
+
f"{next_pending.get('item_label', '')}"
|
|
6365
|
+
)
|
|
6366
|
+
|
|
6367
|
+
defined_work = int(stack_summary["versions"]) + int(stack_summary["milestones"]) + int(stack_summary["phases"]) + int(additional_summary["items"])
|
|
6368
|
+
if (
|
|
6369
|
+
defined_work > 0
|
|
6370
|
+
and not blockers
|
|
6371
|
+
and not terminal_declared
|
|
6372
|
+
and not active_primary
|
|
6373
|
+
and int(additional_summary["active_items"]) == 0
|
|
6374
|
+
and int(additional_summary["pending_items"]) == 0
|
|
6375
|
+
):
|
|
6376
|
+
issues.append(
|
|
6377
|
+
{
|
|
6378
|
+
"severity": "warning",
|
|
6379
|
+
"code": "no_frontier_continuation_or_terminal_declaration",
|
|
6380
|
+
"message": "frontier has defined work but no active/open continuation and no terminal completion declaration.",
|
|
6381
|
+
}
|
|
6382
|
+
)
|
|
6383
|
+
|
|
6384
|
+
summary = {
|
|
6385
|
+
"defined_work": defined_work,
|
|
6386
|
+
"blocked": bool(blockers),
|
|
6387
|
+
"terminal_declared": terminal_declared,
|
|
6388
|
+
"active_primary": active_primary,
|
|
6389
|
+
"active_primary_kind": active_primary_kind,
|
|
6390
|
+
"active_primary_id": active_primary_id,
|
|
6391
|
+
"active_primary_status": active_primary_status,
|
|
6392
|
+
"additional": additional_summary,
|
|
6393
|
+
}
|
|
6394
|
+
return {
|
|
6395
|
+
"ok": _frontier_diagnostic_ok(issues, strict=strict),
|
|
6396
|
+
"strict": strict,
|
|
6397
|
+
"issues": issues,
|
|
6398
|
+
"summary": summary,
|
|
6399
|
+
"next_action": next_action,
|
|
6400
|
+
"suggested_next_command": suggested_next_command,
|
|
6401
|
+
"paths": {key: _path_for_state(value, repo_root) for key, value in paths.items()},
|
|
6402
|
+
}
|
|
6403
|
+
|
|
6404
|
+
|
|
6405
|
+
def _frontier_continuation_payload(repo_root: Path, *, strict: bool = False) -> dict[str, Any]:
|
|
6406
|
+
paths = _frontier_paths(repo_root)
|
|
6407
|
+
stack = _read_json_if_exists(paths["stack_json"])
|
|
6408
|
+
state = _read_json_if_exists(paths["state_json"])
|
|
6409
|
+
return _frontier_build_continuation_payload(repo_root, stack, state, include_structural_issues=True, strict=strict)
|
|
6410
|
+
|
|
6411
|
+
|
|
6412
|
+
def _frontier_additional_item_summary(item_list: dict[str, Any], item: dict[str, Any]) -> str:
|
|
6413
|
+
bits = [
|
|
6414
|
+
f"ORP additional item {str(item_list.get('id', '')).strip()}/{str(item.get('id', '')).strip()}: {str(item.get('label', '')).strip()}",
|
|
6415
|
+
]
|
|
6416
|
+
goal = str(item.get("goal", "")).strip()
|
|
6417
|
+
if goal:
|
|
6418
|
+
bits.append(f"Goal: {goal}")
|
|
6419
|
+
criteria = _coerce_string_list(item.get("success_criteria"))
|
|
6420
|
+
if criteria:
|
|
6421
|
+
bits.append("Success: " + "; ".join(criteria))
|
|
6422
|
+
return " ".join(bits)
|
|
6423
|
+
|
|
6424
|
+
|
|
6425
|
+
def _render_frontier_additional_md(payload: dict[str, Any]) -> str:
|
|
6426
|
+
lines = [
|
|
6427
|
+
f"# Additional Frontier Items: {payload.get('label', '') or payload.get('program_id', '')}",
|
|
6428
|
+
"",
|
|
6429
|
+
f"- active_list_id: `{payload.get('active_list_id', '') or '(none)'}`",
|
|
6430
|
+
f"- active_item_id: `{payload.get('active_item_id', '') or '(none)'}`",
|
|
6431
|
+
"",
|
|
6432
|
+
]
|
|
6433
|
+
lists = payload.get("lists")
|
|
6434
|
+
if isinstance(lists, list) and lists:
|
|
6435
|
+
for item_list in lists:
|
|
6436
|
+
if not isinstance(item_list, dict):
|
|
6437
|
+
continue
|
|
6438
|
+
lines.append(
|
|
6439
|
+
f"## `{item_list.get('id', '')}` {item_list.get('label', '')} (`{item_list.get('status', '') or 'pending'}`)"
|
|
6440
|
+
)
|
|
6441
|
+
lines.append("")
|
|
6442
|
+
items = item_list.get("items")
|
|
6443
|
+
if isinstance(items, list) and items:
|
|
6444
|
+
for item in items:
|
|
6445
|
+
if not isinstance(item, dict):
|
|
6446
|
+
continue
|
|
6447
|
+
marker = "x" if str(item.get("status", "")).strip() == "complete" else " "
|
|
6448
|
+
lines.append(
|
|
6449
|
+
f"- [{marker}] `{item.get('id', '')}` {item.get('label', '')} (`{item.get('status', '') or 'pending'}`)"
|
|
6450
|
+
)
|
|
6451
|
+
goal = str(item.get("goal", "")).strip()
|
|
6452
|
+
if goal:
|
|
6453
|
+
lines.append(f" - goal: {goal}")
|
|
6454
|
+
else:
|
|
6455
|
+
lines.append("- `(no items)`")
|
|
6456
|
+
lines.append("")
|
|
6457
|
+
else:
|
|
6458
|
+
lines.append("- `(no additional lists yet)`")
|
|
6459
|
+
lines.append("")
|
|
6460
|
+
return "\n".join(lines)
|
|
6461
|
+
|
|
6462
|
+
|
|
6463
|
+
def _frontier_write_additional_views(repo_root: Path, payload: dict[str, Any]) -> dict[str, str]:
|
|
6464
|
+
paths = _frontier_paths(repo_root)
|
|
6465
|
+
paths["root"].mkdir(parents=True, exist_ok=True)
|
|
6466
|
+
_frontier_normalize_additional_statuses(payload)
|
|
6467
|
+
_write_json(paths["additional_json"], payload)
|
|
6468
|
+
_write_text(paths["additional_md"], _render_frontier_additional_md(payload) + "\n")
|
|
6469
|
+
return {
|
|
6470
|
+
"additional_json": _path_for_state(paths["additional_json"], repo_root),
|
|
6471
|
+
"additional_md": _path_for_state(paths["additional_md"], repo_root),
|
|
6472
|
+
}
|
|
6473
|
+
|
|
6474
|
+
|
|
5979
6475
|
def _frontier_set_current_frontier(stack: dict[str, Any], state: dict[str, Any]) -> None:
|
|
5980
6476
|
stack["current_frontier"] = {
|
|
5981
6477
|
"active_version": str(state.get("active_version", "")).strip(),
|
|
@@ -6179,6 +6675,11 @@ def _frontier_write_materialized_views(repo_root: Path, stack: dict[str, Any], s
|
|
|
6179
6675
|
_frontier_set_current_frontier(stack, state)
|
|
6180
6676
|
roadmap = _frontier_build_roadmap_payload(stack, state)
|
|
6181
6677
|
checklist = _frontier_build_checklist_payload(stack, state)
|
|
6678
|
+
additional = _frontier_load_additional(repo_root, stack)
|
|
6679
|
+
if not str(additional.get("program_id", "")).strip():
|
|
6680
|
+
additional["program_id"] = str(stack.get("program_id", "")).strip()
|
|
6681
|
+
if not str(additional.get("label", "")).strip():
|
|
6682
|
+
additional["label"] = str(stack.get("label", "")).strip()
|
|
6182
6683
|
_write_json(paths["state_json"], state)
|
|
6183
6684
|
_write_json(paths["stack_json"], stack)
|
|
6184
6685
|
_write_json(paths["roadmap_json"], roadmap)
|
|
@@ -6187,6 +6688,7 @@ def _frontier_write_materialized_views(repo_root: Path, stack: dict[str, Any], s
|
|
|
6187
6688
|
_write_text(paths["roadmap_md"], _render_frontier_roadmap_md(roadmap) + "\n")
|
|
6188
6689
|
_write_text(paths["checklist_md"], _render_frontier_checklist_md(checklist) + "\n")
|
|
6189
6690
|
_write_text(paths["stack_md"], _render_frontier_stack_md(stack) + "\n")
|
|
6691
|
+
_frontier_write_additional_views(repo_root, additional)
|
|
6190
6692
|
return {key: _path_for_state(value, repo_root) for key, value in paths.items() if key != "root"}
|
|
6191
6693
|
|
|
6192
6694
|
|
|
@@ -6245,14 +6747,44 @@ def _frontier_doctor_payload(repo_root: Path) -> dict[str, Any]:
|
|
|
6245
6747
|
)
|
|
6246
6748
|
if band == "exact":
|
|
6247
6749
|
exact_milestones += 1
|
|
6750
|
+
milestone_id = str(milestone_row.get("id", "")).strip()
|
|
6751
|
+
if active_milestone and milestone_id and milestone_id != active_milestone:
|
|
6752
|
+
issues.append(
|
|
6753
|
+
{
|
|
6754
|
+
"severity": "warning",
|
|
6755
|
+
"code": "exact_milestone_not_live",
|
|
6756
|
+
"message": f"milestone `{milestone_id}` is marked exact but the live milestone is `{active_milestone}`.",
|
|
6757
|
+
}
|
|
6758
|
+
)
|
|
6248
6759
|
if exact_milestones > 1:
|
|
6249
6760
|
issues.append(
|
|
6250
6761
|
{"severity": "warning", "code": "multiple_exact_milestones", "message": f"{exact_milestones} milestones are marked exact; the planning rule expects only one live exact milestone."}
|
|
6251
6762
|
)
|
|
6252
6763
|
|
|
6764
|
+
continuation = _frontier_build_continuation_payload(
|
|
6765
|
+
repo_root,
|
|
6766
|
+
stack,
|
|
6767
|
+
state,
|
|
6768
|
+
include_structural_issues=False,
|
|
6769
|
+
)
|
|
6770
|
+
issues.extend(continuation["issues"])
|
|
6771
|
+
else:
|
|
6772
|
+
continuation = _frontier_build_continuation_payload(
|
|
6773
|
+
repo_root,
|
|
6774
|
+
stack if isinstance(stack, dict) else None,
|
|
6775
|
+
state if isinstance(state, dict) else None,
|
|
6776
|
+
include_structural_issues=False,
|
|
6777
|
+
)
|
|
6778
|
+
|
|
6253
6779
|
return {
|
|
6254
|
-
"ok":
|
|
6780
|
+
"ok": _frontier_diagnostic_ok(issues),
|
|
6255
6781
|
"issues": issues,
|
|
6782
|
+
"continuation": {
|
|
6783
|
+
"ok": continuation["ok"],
|
|
6784
|
+
"summary": continuation["summary"],
|
|
6785
|
+
"next_action": continuation["next_action"],
|
|
6786
|
+
"suggested_next_command": continuation["suggested_next_command"],
|
|
6787
|
+
},
|
|
6256
6788
|
"paths": {key: _path_for_state(value, repo_root) for key, value in paths.items()},
|
|
6257
6789
|
}
|
|
6258
6790
|
|
|
@@ -7338,8 +7870,9 @@ def _runner_sync_payload_for_roots(
|
|
|
7338
7870
|
args: argparse.Namespace,
|
|
7339
7871
|
*,
|
|
7340
7872
|
linked_email: str = "",
|
|
7873
|
+
synced_at_utc: str = "",
|
|
7341
7874
|
) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
7342
|
-
synced_at_utc = _now_utc()
|
|
7875
|
+
synced_at_utc = _normalize_timestamp_utc(synced_at_utc, fallback=_now_utc())
|
|
7343
7876
|
linked_projects: list[dict[str, Any]] = []
|
|
7344
7877
|
sessions: list[dict[str, Any]] = []
|
|
7345
7878
|
included_project_roots: list[str] = []
|
|
@@ -8367,12 +8900,15 @@ def _perform_runner_sync_for_roots(
|
|
|
8367
8900
|
args: argparse.Namespace,
|
|
8368
8901
|
session: dict[str, Any],
|
|
8369
8902
|
machine: dict[str, Any],
|
|
8903
|
+
*,
|
|
8904
|
+
synced_at_utc: str = "",
|
|
8370
8905
|
) -> dict[str, Any]:
|
|
8371
8906
|
sync_payload, sync_summary = _runner_sync_payload_for_roots(
|
|
8372
8907
|
repo_roots,
|
|
8373
8908
|
machine,
|
|
8374
8909
|
args,
|
|
8375
8910
|
linked_email=str(session.get("email", "")).strip(),
|
|
8911
|
+
synced_at_utc=synced_at_utc,
|
|
8376
8912
|
)
|
|
8377
8913
|
if not sync_payload["linkedProjects"]:
|
|
8378
8914
|
raise RuntimeError("No linked project is available to sync.")
|
|
@@ -8722,9 +9258,11 @@ def _run_runner_work_once(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
8722
9258
|
)
|
|
8723
9259
|
|
|
8724
9260
|
if run_result.get("ok"):
|
|
9261
|
+
completed_at_utc = _now_utc()
|
|
8725
9262
|
touched_session = _touch_link_session_last_active(
|
|
8726
9263
|
selected_repo_root,
|
|
8727
9264
|
str(selected_session.get("orp_session_id", "")).strip(),
|
|
9265
|
+
timestamp_utc=completed_at_utc,
|
|
8728
9266
|
)
|
|
8729
9267
|
try:
|
|
8730
9268
|
sync_result = _perform_runner_sync_for_roots(
|
|
@@ -8733,6 +9271,7 @@ def _run_runner_work_once(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
8733
9271
|
args,
|
|
8734
9272
|
session,
|
|
8735
9273
|
machine,
|
|
9274
|
+
synced_at_utc=completed_at_utc,
|
|
8736
9275
|
)
|
|
8737
9276
|
except Exception as exc:
|
|
8738
9277
|
sync_error = str(exc)
|
|
@@ -10762,12 +11301,16 @@ def _about_payload() -> dict[str, Any]:
|
|
|
10762
11301
|
},
|
|
10763
11302
|
{
|
|
10764
11303
|
"id": "frontier",
|
|
10765
|
-
"description": "Version-stack, milestone, phase, and
|
|
11304
|
+
"description": "Version-stack, milestone, phase, additional-queue, and continuation-preflight control for long-running agent-first research programs.",
|
|
10766
11305
|
"entrypoints": [
|
|
10767
11306
|
["frontier", "init"],
|
|
10768
11307
|
["frontier", "state"],
|
|
10769
11308
|
["frontier", "roadmap"],
|
|
10770
11309
|
["frontier", "checklist"],
|
|
11310
|
+
["frontier", "continuation-status"],
|
|
11311
|
+
["frontier", "preflight-delegate"],
|
|
11312
|
+
["frontier", "additional", "list"],
|
|
11313
|
+
["frontier", "additional", "activate-next"],
|
|
10771
11314
|
["frontier", "stack"],
|
|
10772
11315
|
["frontier", "add-version"],
|
|
10773
11316
|
["frontier", "add-milestone"],
|
|
@@ -10935,6 +11478,13 @@ def _about_payload() -> dict[str, Any]:
|
|
|
10935
11478
|
{"name": "frontier_state", "path": ["frontier", "state"], "json_output": True},
|
|
10936
11479
|
{"name": "frontier_roadmap", "path": ["frontier", "roadmap"], "json_output": True},
|
|
10937
11480
|
{"name": "frontier_checklist", "path": ["frontier", "checklist"], "json_output": True},
|
|
11481
|
+
{"name": "frontier_continuation_status", "path": ["frontier", "continuation-status"], "json_output": True},
|
|
11482
|
+
{"name": "frontier_preflight_delegate", "path": ["frontier", "preflight-delegate"], "json_output": True},
|
|
11483
|
+
{"name": "frontier_additional_list", "path": ["frontier", "additional", "list"], "json_output": True},
|
|
11484
|
+
{"name": "frontier_additional_add_list", "path": ["frontier", "additional", "add-list"], "json_output": True},
|
|
11485
|
+
{"name": "frontier_additional_add_item", "path": ["frontier", "additional", "add-item"], "json_output": True},
|
|
11486
|
+
{"name": "frontier_additional_activate_next", "path": ["frontier", "additional", "activate-next"], "json_output": True},
|
|
11487
|
+
{"name": "frontier_additional_complete_active", "path": ["frontier", "additional", "complete-active"], "json_output": True},
|
|
10938
11488
|
{"name": "frontier_stack", "path": ["frontier", "stack"], "json_output": True},
|
|
10939
11489
|
{"name": "frontier_add_version", "path": ["frontier", "add-version"], "json_output": True},
|
|
10940
11490
|
{"name": "frontier_add_milestone", "path": ["frontier", "add-milestone"], "json_output": True},
|
|
@@ -10966,7 +11516,7 @@ def _about_payload() -> dict[str, Any]:
|
|
|
10966
11516
|
"Discovery profiles in ORP are portable search-intent files managed directly by ORP.",
|
|
10967
11517
|
"Knowledge exchange is a built-in ORP ability exposed through `orp exchange repo synthesize`, producing structured exchange artifacts and transfer maps for local or remote source repositories.",
|
|
10968
11518
|
"Collaboration is a built-in ORP ability exposed through `orp collaborate ...`.",
|
|
10969
|
-
"Frontier control is a built-in ORP ability exposed through `orp frontier ...`, separating the exact live point, the exact active milestone, the near structured checklist, and
|
|
11519
|
+
"Frontier control is a built-in ORP ability exposed through `orp frontier ...`, separating the exact live point, the exact active milestone, the near structured checklist, the additional work queue, and strict continuation preflight before delegation.",
|
|
10970
11520
|
"Agent modes are lightweight optional overlays for taste, perspective shifts, fresh movement, and intentional comprehension breakdowns; `orp mode breakdown granular-breakdown --json` gives agents a broad-to-atomic ladder for complex work, while `orp mode nudge granular-breakdown --json` gives a short reminder card.",
|
|
10971
11521
|
"Project/session linking is a built-in ORP ability exposed through `orp link ...` and stored machine-locally under `.git/orp/link/`.",
|
|
10972
11522
|
"Secrets are easiest to understand as saved credentials and related login metadata: humans usually run `orp secrets add ...` and paste the value at the prompt, agents usually pipe the value with `--value-stdin`, optional usernames can be stored alongside the secret when a service needs them, and local macOS Keychain caching plus hosted sync are optional layers on top.",
|
|
@@ -13525,6 +14075,245 @@ def cmd_frontier_checklist(args: argparse.Namespace) -> int:
|
|
|
13525
14075
|
return 0
|
|
13526
14076
|
|
|
13527
14077
|
|
|
14078
|
+
def cmd_frontier_additional_list(args: argparse.Namespace) -> int:
|
|
14079
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14080
|
+
stack = _frontier_load_stack(repo_root)
|
|
14081
|
+
payload = _frontier_load_additional(repo_root, stack)
|
|
14082
|
+
lists = payload.get("lists")
|
|
14083
|
+
rows = lists if isinstance(lists, list) else []
|
|
14084
|
+
pending_items = 0
|
|
14085
|
+
active_items = 0
|
|
14086
|
+
complete_items = 0
|
|
14087
|
+
for item_list in rows:
|
|
14088
|
+
if not isinstance(item_list, dict):
|
|
14089
|
+
continue
|
|
14090
|
+
items = item_list.get("items")
|
|
14091
|
+
if not isinstance(items, list):
|
|
14092
|
+
continue
|
|
14093
|
+
for item in items:
|
|
14094
|
+
if not isinstance(item, dict):
|
|
14095
|
+
continue
|
|
14096
|
+
status = str(item.get("status", "")).strip() or "pending"
|
|
14097
|
+
if status == "active":
|
|
14098
|
+
active_items += 1
|
|
14099
|
+
elif status == "complete":
|
|
14100
|
+
complete_items += 1
|
|
14101
|
+
else:
|
|
14102
|
+
pending_items += 1
|
|
14103
|
+
result = {
|
|
14104
|
+
"ok": True,
|
|
14105
|
+
**payload,
|
|
14106
|
+
"summary": {
|
|
14107
|
+
"lists": len(rows),
|
|
14108
|
+
"pending_items": pending_items,
|
|
14109
|
+
"active_items": active_items,
|
|
14110
|
+
"complete_items": complete_items,
|
|
14111
|
+
},
|
|
14112
|
+
"paths": {
|
|
14113
|
+
"additional_json": _path_for_state(_frontier_paths(repo_root)["additional_json"], repo_root),
|
|
14114
|
+
"additional_md": _path_for_state(_frontier_paths(repo_root)["additional_md"], repo_root),
|
|
14115
|
+
},
|
|
14116
|
+
}
|
|
14117
|
+
if args.json_output:
|
|
14118
|
+
_print_json(result)
|
|
14119
|
+
else:
|
|
14120
|
+
print(f"lists={len(rows)}")
|
|
14121
|
+
print(f"active_list_id={payload.get('active_list_id', '') or '(none)'}")
|
|
14122
|
+
print(f"active_item_id={payload.get('active_item_id', '') or '(none)'}")
|
|
14123
|
+
print(f"pending_items={pending_items}")
|
|
14124
|
+
print(f"active_items={active_items}")
|
|
14125
|
+
print(f"complete_items={complete_items}")
|
|
14126
|
+
return 0
|
|
14127
|
+
|
|
14128
|
+
|
|
14129
|
+
def cmd_frontier_additional_add_list(args: argparse.Namespace) -> int:
|
|
14130
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14131
|
+
stack = _frontier_load_stack(repo_root)
|
|
14132
|
+
payload = _frontier_load_additional(repo_root, stack)
|
|
14133
|
+
list_id = str(args.id).strip()
|
|
14134
|
+
if _frontier_find_additional_list(payload, list_id) is not None:
|
|
14135
|
+
raise RuntimeError(f"frontier additional list `{list_id}` already exists.")
|
|
14136
|
+
item_list = {
|
|
14137
|
+
"id": list_id,
|
|
14138
|
+
"label": str(args.label).strip(),
|
|
14139
|
+
"status": str(args.status or "pending").strip() or "pending",
|
|
14140
|
+
"items": [],
|
|
14141
|
+
}
|
|
14142
|
+
lists = payload.get("lists")
|
|
14143
|
+
if not isinstance(lists, list):
|
|
14144
|
+
lists = []
|
|
14145
|
+
payload["lists"] = lists
|
|
14146
|
+
lists.append(item_list)
|
|
14147
|
+
written = _frontier_write_additional_views(repo_root, payload)
|
|
14148
|
+
result = {"ok": True, "list": item_list, "paths": written}
|
|
14149
|
+
if args.json_output:
|
|
14150
|
+
_print_json(result)
|
|
14151
|
+
else:
|
|
14152
|
+
print(f"list_id={list_id}")
|
|
14153
|
+
print(f"label={item_list['label']}")
|
|
14154
|
+
return 0
|
|
14155
|
+
|
|
14156
|
+
|
|
14157
|
+
def cmd_frontier_additional_add_item(args: argparse.Namespace) -> int:
|
|
14158
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14159
|
+
stack = _frontier_load_stack(repo_root)
|
|
14160
|
+
payload = _frontier_load_additional(repo_root, stack)
|
|
14161
|
+
list_id = str(args.list).strip()
|
|
14162
|
+
item_list = _frontier_find_additional_list(payload, list_id)
|
|
14163
|
+
if item_list is None:
|
|
14164
|
+
raise RuntimeError(f"frontier additional list `{list_id}` was not found.")
|
|
14165
|
+
item_id = str(args.id).strip()
|
|
14166
|
+
if _frontier_find_additional_item(item_list, item_id) is not None:
|
|
14167
|
+
raise RuntimeError(f"frontier additional item `{item_id}` already exists in list `{list_id}`.")
|
|
14168
|
+
item = {
|
|
14169
|
+
"id": item_id,
|
|
14170
|
+
"label": str(args.label).strip(),
|
|
14171
|
+
"status": str(args.status or "pending").strip() or "pending",
|
|
14172
|
+
"goal": str(args.goal or "").strip(),
|
|
14173
|
+
"depends_on": _coerce_string_list(getattr(args, "depends_on", [])),
|
|
14174
|
+
"requirements": _coerce_string_list(getattr(args, "requirement", [])),
|
|
14175
|
+
"success_criteria": _coerce_string_list(getattr(args, "success_criterion", [])),
|
|
14176
|
+
"plans": _coerce_string_list(getattr(args, "plan", [])),
|
|
14177
|
+
}
|
|
14178
|
+
items = item_list.get("items")
|
|
14179
|
+
if not isinstance(items, list):
|
|
14180
|
+
items = []
|
|
14181
|
+
item_list["items"] = items
|
|
14182
|
+
items.append(item)
|
|
14183
|
+
written = _frontier_write_additional_views(repo_root, payload)
|
|
14184
|
+
result = {"ok": True, "list_id": list_id, "item": item, "paths": written}
|
|
14185
|
+
if args.json_output:
|
|
14186
|
+
_print_json(result)
|
|
14187
|
+
else:
|
|
14188
|
+
print(f"list_id={list_id}")
|
|
14189
|
+
print(f"item_id={item_id}")
|
|
14190
|
+
print(f"label={item['label']}")
|
|
14191
|
+
return 0
|
|
14192
|
+
|
|
14193
|
+
|
|
14194
|
+
def cmd_frontier_additional_activate_next(args: argparse.Namespace) -> int:
|
|
14195
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14196
|
+
stack = _frontier_load_stack(repo_root)
|
|
14197
|
+
payload = _frontier_load_additional(repo_root, stack)
|
|
14198
|
+
active_list, active_item = _frontier_active_additional_item(payload)
|
|
14199
|
+
already_active = active_list is not None and active_item is not None and str(active_item.get("status", "")).strip() == "active"
|
|
14200
|
+
if not already_active:
|
|
14201
|
+
active_list, active_item = _frontier_next_pending_additional_item(payload)
|
|
14202
|
+
if active_list is not None and active_item is not None:
|
|
14203
|
+
active_list["status"] = "active"
|
|
14204
|
+
active_item["status"] = "active"
|
|
14205
|
+
payload["active_list_id"] = str(active_list.get("id", "")).strip()
|
|
14206
|
+
payload["active_item_id"] = str(active_item.get("id", "")).strip()
|
|
14207
|
+
else:
|
|
14208
|
+
payload["active_list_id"] = ""
|
|
14209
|
+
payload["active_item_id"] = ""
|
|
14210
|
+
written = _frontier_write_additional_views(repo_root, payload)
|
|
14211
|
+
activated = active_list is not None and active_item is not None
|
|
14212
|
+
result = {
|
|
14213
|
+
"ok": True,
|
|
14214
|
+
"activated": activated,
|
|
14215
|
+
"already_active": already_active,
|
|
14216
|
+
"active_list_id": payload.get("active_list_id", ""),
|
|
14217
|
+
"active_item_id": payload.get("active_item_id", ""),
|
|
14218
|
+
"list": active_list if activated else None,
|
|
14219
|
+
"item": active_item if activated else None,
|
|
14220
|
+
"next_action": _frontier_additional_item_summary(active_list, active_item) if activated else "",
|
|
14221
|
+
"paths": written,
|
|
14222
|
+
}
|
|
14223
|
+
if args.json_output:
|
|
14224
|
+
_print_json(result)
|
|
14225
|
+
else:
|
|
14226
|
+
print(f"activated={'true' if activated else 'false'}")
|
|
14227
|
+
if activated:
|
|
14228
|
+
print(f"active_list_id={result['active_list_id']}")
|
|
14229
|
+
print(f"active_item_id={result['active_item_id']}")
|
|
14230
|
+
print(f"next_action={result['next_action']}")
|
|
14231
|
+
return 0
|
|
14232
|
+
|
|
14233
|
+
|
|
14234
|
+
def cmd_frontier_additional_complete_active(args: argparse.Namespace) -> int:
|
|
14235
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14236
|
+
stack = _frontier_load_stack(repo_root)
|
|
14237
|
+
payload = _frontier_load_additional(repo_root, stack)
|
|
14238
|
+
active_list, active_item = _frontier_active_additional_item(payload)
|
|
14239
|
+
completed = active_list is not None and active_item is not None
|
|
14240
|
+
list_completed = False
|
|
14241
|
+
if completed:
|
|
14242
|
+
active_item["status"] = str(args.status or "complete").strip() or "complete"
|
|
14243
|
+
items = active_list.get("items")
|
|
14244
|
+
if isinstance(items, list) and all(
|
|
14245
|
+
str(item.get("status", "")).strip() in {"complete", "skipped"} for item in items if isinstance(item, dict)
|
|
14246
|
+
):
|
|
14247
|
+
active_list["status"] = "complete"
|
|
14248
|
+
list_completed = True
|
|
14249
|
+
payload["active_list_id"] = ""
|
|
14250
|
+
payload["active_item_id"] = ""
|
|
14251
|
+
written = _frontier_write_additional_views(repo_root, payload)
|
|
14252
|
+
result = {
|
|
14253
|
+
"ok": True,
|
|
14254
|
+
"completed": completed,
|
|
14255
|
+
"list_completed": list_completed,
|
|
14256
|
+
"list": active_list if completed else None,
|
|
14257
|
+
"item": active_item if completed else None,
|
|
14258
|
+
"paths": written,
|
|
14259
|
+
}
|
|
14260
|
+
if args.json_output:
|
|
14261
|
+
_print_json(result)
|
|
14262
|
+
else:
|
|
14263
|
+
print(f"completed={'true' if completed else 'false'}")
|
|
14264
|
+
print(f"list_completed={'true' if list_completed else 'false'}")
|
|
14265
|
+
return 0
|
|
14266
|
+
|
|
14267
|
+
|
|
14268
|
+
def _print_frontier_diagnostic_payload(payload: dict[str, Any]) -> None:
|
|
14269
|
+
print(f"ok={'true' if payload.get('ok') else 'false'}")
|
|
14270
|
+
print(f"next_action={payload.get('next_action', '') or '(none)'}")
|
|
14271
|
+
suggested = str(payload.get("suggested_next_command", "")).strip()
|
|
14272
|
+
if suggested:
|
|
14273
|
+
print(f"suggested_next_command={suggested}")
|
|
14274
|
+
summary = payload.get("summary")
|
|
14275
|
+
if isinstance(summary, dict):
|
|
14276
|
+
print(f"active_primary={summary.get('active_primary_kind', '') or '(none)'}:{summary.get('active_primary_id', '') or '(none)'}")
|
|
14277
|
+
additional = summary.get("additional")
|
|
14278
|
+
if isinstance(additional, dict):
|
|
14279
|
+
print(f"pending_additional_items={additional.get('pending_items', 0)}")
|
|
14280
|
+
print(f"active_additional={additional.get('active_list_id', '') or '(none)'}/{additional.get('active_item_id', '') or '(none)'}")
|
|
14281
|
+
for issue in payload.get("issues", []):
|
|
14282
|
+
if isinstance(issue, dict):
|
|
14283
|
+
print(f"issue={issue.get('severity','')}:{issue.get('code','')}:{issue.get('message','')}")
|
|
14284
|
+
|
|
14285
|
+
|
|
14286
|
+
def cmd_frontier_continuation_status(args: argparse.Namespace) -> int:
|
|
14287
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14288
|
+
strict = bool(getattr(args, "strict", False))
|
|
14289
|
+
payload = _frontier_continuation_payload(repo_root, strict=strict)
|
|
14290
|
+
if args.json_output:
|
|
14291
|
+
_print_json(payload)
|
|
14292
|
+
else:
|
|
14293
|
+
_print_frontier_diagnostic_payload(payload)
|
|
14294
|
+
return 0 if payload["ok"] else 1
|
|
14295
|
+
|
|
14296
|
+
|
|
14297
|
+
def cmd_frontier_preflight_delegate(args: argparse.Namespace) -> int:
|
|
14298
|
+
repo_root = Path(args.repo_root).resolve()
|
|
14299
|
+
payload = _frontier_doctor_payload(repo_root)
|
|
14300
|
+
payload["strict"] = True
|
|
14301
|
+
payload["ok"] = _frontier_diagnostic_ok(payload.get("issues", []), strict=True)
|
|
14302
|
+
continuation = payload.get("continuation") if isinstance(payload.get("continuation"), dict) else {}
|
|
14303
|
+
payload["next_action"] = continuation.get("next_action", "")
|
|
14304
|
+
payload["suggested_next_command"] = continuation.get("suggested_next_command", "")
|
|
14305
|
+
payload["summary"] = continuation.get("summary", {})
|
|
14306
|
+
payload["preflight"] = {
|
|
14307
|
+
"ready": bool(payload["ok"]),
|
|
14308
|
+
"purpose": "Block delegation when the frontier cannot prove a single safe continuation or terminal state.",
|
|
14309
|
+
}
|
|
14310
|
+
if args.json_output:
|
|
14311
|
+
_print_json(payload)
|
|
14312
|
+
else:
|
|
14313
|
+
_print_frontier_diagnostic_payload(payload)
|
|
14314
|
+
return 0 if payload["ok"] else 1
|
|
14315
|
+
|
|
14316
|
+
|
|
13528
14317
|
def cmd_frontier_add_version(args: argparse.Namespace) -> int:
|
|
13529
14318
|
repo_root = Path(args.repo_root).resolve()
|
|
13530
14319
|
stack = _frontier_load_stack(repo_root)
|
|
@@ -13732,6 +14521,9 @@ def cmd_frontier_render(args: argparse.Namespace) -> int:
|
|
|
13732
14521
|
def cmd_frontier_doctor(args: argparse.Namespace) -> int:
|
|
13733
14522
|
repo_root = Path(args.repo_root).resolve()
|
|
13734
14523
|
payload = _frontier_doctor_payload(repo_root)
|
|
14524
|
+
strict = bool(getattr(args, "strict", False))
|
|
14525
|
+
payload["strict"] = strict
|
|
14526
|
+
payload["ok"] = _frontier_diagnostic_ok(payload.get("issues", []), strict=strict)
|
|
13735
14527
|
fixes_applied: list[str] = []
|
|
13736
14528
|
if args.fix and payload["ok"]:
|
|
13737
14529
|
stack = _frontier_load_stack(repo_root)
|
|
@@ -24585,6 +25377,114 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
24585
25377
|
add_json_flag(s_frontier_checklist)
|
|
24586
25378
|
s_frontier_checklist.set_defaults(func=cmd_frontier_checklist, json_output=False)
|
|
24587
25379
|
|
|
25380
|
+
s_frontier_continuation_status = frontier_sub.add_parser(
|
|
25381
|
+
"continuation-status",
|
|
25382
|
+
help="Show whether the frontier has a safe next continuation, blocker, or terminal declaration",
|
|
25383
|
+
)
|
|
25384
|
+
s_frontier_continuation_status.add_argument(
|
|
25385
|
+
"--strict",
|
|
25386
|
+
action="store_true",
|
|
25387
|
+
help="Return non-zero when continuation warnings are present",
|
|
25388
|
+
)
|
|
25389
|
+
add_json_flag(s_frontier_continuation_status)
|
|
25390
|
+
s_frontier_continuation_status.set_defaults(func=cmd_frontier_continuation_status, json_output=False)
|
|
25391
|
+
|
|
25392
|
+
s_frontier_preflight_delegate = frontier_sub.add_parser(
|
|
25393
|
+
"preflight-delegate",
|
|
25394
|
+
help="Fail delegation when frontier continuation is stale, ambiguous, or not activated",
|
|
25395
|
+
)
|
|
25396
|
+
add_json_flag(s_frontier_preflight_delegate)
|
|
25397
|
+
s_frontier_preflight_delegate.set_defaults(func=cmd_frontier_preflight_delegate, json_output=False)
|
|
25398
|
+
|
|
25399
|
+
s_frontier_additional = frontier_sub.add_parser(
|
|
25400
|
+
"additional",
|
|
25401
|
+
help="Manage queued additional Frontier work that should run after the active delegate objective",
|
|
25402
|
+
)
|
|
25403
|
+
frontier_additional_sub = s_frontier_additional.add_subparsers(dest="frontier_additional_cmd", required=True)
|
|
25404
|
+
|
|
25405
|
+
s_frontier_additional_list = frontier_additional_sub.add_parser(
|
|
25406
|
+
"list",
|
|
25407
|
+
help="Show additional Frontier work lists and their item states",
|
|
25408
|
+
)
|
|
25409
|
+
add_json_flag(s_frontier_additional_list)
|
|
25410
|
+
s_frontier_additional_list.set_defaults(func=cmd_frontier_additional_list, json_output=False)
|
|
25411
|
+
|
|
25412
|
+
s_frontier_additional_add_list = frontier_additional_sub.add_parser(
|
|
25413
|
+
"add-list",
|
|
25414
|
+
help="Add an additional work list that delegates may drain after the active objective",
|
|
25415
|
+
)
|
|
25416
|
+
s_frontier_additional_add_list.add_argument("--id", required=True, help="Additional list id")
|
|
25417
|
+
s_frontier_additional_add_list.add_argument("--label", required=True, help="Additional list label")
|
|
25418
|
+
s_frontier_additional_add_list.add_argument(
|
|
25419
|
+
"--status",
|
|
25420
|
+
default="pending",
|
|
25421
|
+
choices=["pending", "active", "complete"],
|
|
25422
|
+
help="List status (default: pending)",
|
|
25423
|
+
)
|
|
25424
|
+
add_json_flag(s_frontier_additional_add_list)
|
|
25425
|
+
s_frontier_additional_add_list.set_defaults(func=cmd_frontier_additional_add_list, json_output=False)
|
|
25426
|
+
|
|
25427
|
+
s_frontier_additional_add_item = frontier_additional_sub.add_parser(
|
|
25428
|
+
"add-item",
|
|
25429
|
+
help="Add one item to an additional Frontier work list",
|
|
25430
|
+
)
|
|
25431
|
+
s_frontier_additional_add_item.add_argument("--list", required=True, help="Parent additional list id")
|
|
25432
|
+
s_frontier_additional_add_item.add_argument("--id", required=True, help="Additional item id")
|
|
25433
|
+
s_frontier_additional_add_item.add_argument("--label", required=True, help="Additional item label")
|
|
25434
|
+
s_frontier_additional_add_item.add_argument(
|
|
25435
|
+
"--status",
|
|
25436
|
+
default="pending",
|
|
25437
|
+
choices=["pending", "active", "complete", "skipped"],
|
|
25438
|
+
help="Item status (default: pending)",
|
|
25439
|
+
)
|
|
25440
|
+
s_frontier_additional_add_item.add_argument("--goal", default="", help="Optional item goal")
|
|
25441
|
+
s_frontier_additional_add_item.add_argument(
|
|
25442
|
+
"--depends-on",
|
|
25443
|
+
action="append",
|
|
25444
|
+
default=[],
|
|
25445
|
+
help="Dependency item, phase, or milestone id (repeatable)",
|
|
25446
|
+
)
|
|
25447
|
+
s_frontier_additional_add_item.add_argument(
|
|
25448
|
+
"--requirement",
|
|
25449
|
+
action="append",
|
|
25450
|
+
default=[],
|
|
25451
|
+
help="Requirement identifier or note (repeatable)",
|
|
25452
|
+
)
|
|
25453
|
+
s_frontier_additional_add_item.add_argument(
|
|
25454
|
+
"--success-criterion",
|
|
25455
|
+
action="append",
|
|
25456
|
+
default=[],
|
|
25457
|
+
help="Item success criterion (repeatable)",
|
|
25458
|
+
)
|
|
25459
|
+
s_frontier_additional_add_item.add_argument(
|
|
25460
|
+
"--plan",
|
|
25461
|
+
action="append",
|
|
25462
|
+
default=[],
|
|
25463
|
+
help="Plan id or plan note (repeatable)",
|
|
25464
|
+
)
|
|
25465
|
+
add_json_flag(s_frontier_additional_add_item)
|
|
25466
|
+
s_frontier_additional_add_item.set_defaults(func=cmd_frontier_additional_add_item, json_output=False)
|
|
25467
|
+
|
|
25468
|
+
s_frontier_additional_activate_next = frontier_additional_sub.add_parser(
|
|
25469
|
+
"activate-next",
|
|
25470
|
+
help="Activate and print the next pending additional Frontier item",
|
|
25471
|
+
)
|
|
25472
|
+
add_json_flag(s_frontier_additional_activate_next)
|
|
25473
|
+
s_frontier_additional_activate_next.set_defaults(func=cmd_frontier_additional_activate_next, json_output=False)
|
|
25474
|
+
|
|
25475
|
+
s_frontier_additional_complete_active = frontier_additional_sub.add_parser(
|
|
25476
|
+
"complete-active",
|
|
25477
|
+
help="Mark the active additional Frontier item complete",
|
|
25478
|
+
)
|
|
25479
|
+
s_frontier_additional_complete_active.add_argument(
|
|
25480
|
+
"--status",
|
|
25481
|
+
default="complete",
|
|
25482
|
+
choices=["complete", "skipped"],
|
|
25483
|
+
help="Completion status for the active item (default: complete)",
|
|
25484
|
+
)
|
|
25485
|
+
add_json_flag(s_frontier_additional_complete_active)
|
|
25486
|
+
s_frontier_additional_complete_active.set_defaults(func=cmd_frontier_additional_complete_active, json_output=False)
|
|
25487
|
+
|
|
24588
25488
|
s_frontier_stack = frontier_sub.add_parser(
|
|
24589
25489
|
"stack",
|
|
24590
25490
|
help="Show the larger major-version stack",
|
|
@@ -24738,6 +25638,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
24738
25638
|
action="store_true",
|
|
24739
25639
|
help="Re-render materialized frontier views when the frontier is otherwise consistent",
|
|
24740
25640
|
)
|
|
25641
|
+
s_frontier_doctor.add_argument(
|
|
25642
|
+
"--strict",
|
|
25643
|
+
action="store_true",
|
|
25644
|
+
help="Return non-zero when frontier warnings are present",
|
|
25645
|
+
)
|
|
24741
25646
|
add_json_flag(s_frontier_doctor)
|
|
24742
25647
|
s_frontier_doctor.set_defaults(func=cmd_frontier_doctor, json_output=False)
|
|
24743
25648
|
|