gabriel / muse public
_args.py python
717 lines 32.1 KB
Raw
sha256:37af4ab271a64f11d7bf14b4871ebaaf5dd68fb0247521007c6b034d35aa01be Merge branch 'fix/hub-user-read-update-path' into dev Human 10 days ago
1 """muse harmony — Resolution Intelligence for domain-agnostic conflict resolution.
2
3 The harmony engine provides three-tier resolution intelligence:
4
5 1. Policy match — a declarative rule fires automatically.
6 2. Exact replay — blob fingerprint matches a saved resolution.
7 3. Semantic match — domain-plugin similarity score ≥ threshold.
8 4. Escalate — create a hub issue, flag for human or specialist agent.
9
10 Subcommands
11 -----------
12
13 ``muse harmony record`` Record a new conflict pattern.
14 ``muse harmony list`` List all recorded conflict patterns.
15 ``muse harmony show`` Show a pattern and all its resolutions.
16 ``muse harmony resolve`` Save a resolution for a conflict pattern.
17 ``muse harmony best`` Show the highest-quality resolution for a pattern.
18 ``muse harmony forget`` Remove a conflict pattern and all its resolutions.
19 ``muse harmony clear`` Remove all conflict patterns.
20 ``muse harmony gc`` Garbage-collect stale unresolved patterns.
21 ``muse harmony policy-add`` Add or replace a declarative resolution policy.
22 ``muse harmony policy-list`` List all policies (scope-sorted).
23 ``muse harmony policy-remove`` Remove a policy.
24 ``muse harmony audit`` Show the harmony audit log.
25
26 Storage
27 -------
28
29 ``.muse/harmony/patterns/<pattern_id>/pattern.json``
30 ``.muse/harmony/patterns/<pattern_id>/resolutions/<resolution_id>.json``
31 ``.muse/harmony/policies/<policy_id>.json``
32 ``.muse/harmony/audit/<YYYYMMDD>-<content-id>.json``
33
34 Security model
35 --------------
36
37 Pattern IDs and resolution IDs are validated as exactly 64 lowercase hex
38 characters before any filesystem path is constructed — preventing ``../``
39 path-traversal. Policy IDs are validated as URL-safe alphanumeric strings.
40 Error messages go to **stderr**; **stdout** carries only data.
41
42 Agent UX
43 --------
44
45 Pass ``--json`` to any subcommand for machine-readable output. JSON schemas
46 are defined by TypedDicts in ``_shapes.py`` — all fields are always present.
47
48 JSON schemas
49 ~~~~~~~~~~~~
50
51 ``muse harmony record --json``::
52
53 {"pattern_id": "<hex64>", "already_existed": <bool>}
54
55 ``muse harmony list --json``::
56
57 {
58 "total": <int>,
59 "patterns": [
60 {
61 "pattern_id": "<hex64>",
62 "path": "<str>",
63 "domain": "<str>",
64 "conflict_type": "<str>",
65 "resolution_count": <int>,
66 "recorded_at": "<iso8601>",
67 "recorded_by": "<str>"
68 }, ...
69 ]
70 }
71
72 ``muse harmony show <pattern_id> --json``::
73
74 {
75 "pattern": {
76 "pattern_id": "<hex64>", "path": "<str>", "domain": "<str>",
77 "conflict_type": "<str>", "blob_fingerprint": "<hex64>",
78 "semantic_fingerprint": "<hex64>", "ours_id": "<hex64>",
79 "theirs_id": "<hex64>", "description": {}, "recorded_at": "<iso8601>",
80 "recorded_by": "<str>"
81 },
82 "resolutions": [
83 {
84 "resolution_id": "<hex64>", "strategy": "<str>",
85 "confidence": <float>, "human_verified": <bool>,
86 "applied_count": <int>, "resolved_by": {"type": "...", ...},
87 "resolved_at": "<iso8601>", "rationale": "<str>"
88 }, ...
89 ]
90 }
91
92 ``muse harmony resolve --json``::
93
94 {"resolution_id": "<hex64>", "pattern_id": "<hex64>", "already_existed": <bool>}
95
96 ``muse harmony best <pattern_id> --json``::
97
98 {"pattern_id": "<hex64>", "resolution": {<resolution object> | null}}
99
100 ``muse harmony forget <pattern_id> --json``::
101
102 {"pattern_id": "<hex64>", "removed": <bool>}
103
104 ``muse harmony clear --json`` / ``muse harmony gc --json``::
105
106 {"removed": <int>} (gc also includes "age_days": <int>)
107
108 ``muse harmony policy-add --json``::
109
110 {"policy_id": "<str>", "action": "<str>", "scope": "<str>"}
111
112 ``muse harmony policy-list --json``::
113
114 {
115 "total": <int>,
116 "policies": [
117 {
118 "policy_id": "<str>", "description": "<str>", "scope": "<str>",
119 "action": "<str>", "confidence": <float>,
120 "conflict_type": "<str>|null", "domain": "<str>|null",
121 "path_pattern": "<str>|null", "created_at": "<iso8601>",
122 "created_by": "<str>"
123 }, ...
124 ]
125 }
126
127 ``muse harmony policy-remove <policy_id> --json``::
128
129 {"policy_id": "<str>", "removed": <bool>}
130
131 ``muse harmony audit --json``::
132
133 {"total": <int>, "entries": [{<AuditEvent>}, ...]}
134
135 Exit codes
136 ----------
137
138 - 0 — success
139 - 1 — invalid arguments, not-found, or validation failure
140 - 2 — not inside a Muse repository
141 """
142
143 from __future__ import annotations
144
145 import argparse
146
147 from ._handlers import (
148 run_audit,
149 run_best,
150 run_clear,
151 run_engine,
152 run_escalate,
153 run_escalations,
154 run_forget,
155 run_gc,
156 run_list,
157 run_policy_add,
158 run_policy_list,
159 run_policy_remove,
160 run_record,
161 run_resolve,
162 run_resolve_escalation,
163 run_show,
164 run_similar,
165 )
166
167
168 def register(
169 subparsers: "argparse._SubParsersAction[argparse.ArgumentParser]",
170 ) -> None:
171 """Register the harmony subcommand group."""
172 parser = subparsers.add_parser(
173 "harmony",
174 help="Resolution Intelligence — record, resolve, and replay conflict patterns.",
175 description=__doc__,
176 formatter_class=argparse.RawDescriptionHelpFormatter,
177 )
178 subs = parser.add_subparsers(dest="subcommand", metavar="SUBCOMMAND")
179 subs.required = True
180
181 # -- record ---------------------------------------------------------------
182 record_p = subs.add_parser(
183 "record",
184 help="Record a new conflict pattern.",
185 description=(
186 "Persist a ConflictPattern to the harmony store.\n\n"
187 "The pattern is identified by a blob fingerprint (SHA-256 of sorted\n"
188 "ours_id:theirs_id) and an optional semantic fingerprint supplied by\n"
189 "a domain plugin. Recording the same pattern twice is idempotent.\n\n"
190 "Agent quickstart\n"
191 "----------------\n"
192 " muse harmony record --path track.mid --domain midi \\\n"
193 " --conflict-type content \\\n"
194 " --ours-id <hex64> --theirs-id <hex64> --json\n\n"
195 "JSON output schema\n"
196 "------------------\n"
197 ' {"pattern_id": "<hex64>", "already_existed": <bool>}\n\n'
198 "Exit codes\n"
199 "----------\n"
200 " 0 — pattern recorded (or already existed)\n"
201 " 1 — invalid arguments or validation failure\n"
202 " 2 — not inside a Muse repository\n"
203 ),
204 formatter_class=argparse.RawDescriptionHelpFormatter,
205 )
206 record_p.add_argument("--path", required=True,
207 help="Workspace-relative POSIX path of the conflicting file.")
208 record_p.add_argument("--domain", required=True,
209 help="Domain name, e.g. 'midi' or 'code'.")
210 record_p.add_argument("--conflict-type", required=True, dest="conflict_type",
211 help="Conflict category (content, structural, metadata, relational, ...).")
212 record_p.add_argument("--ours-id", required=True, dest="ours_id",
213 help="64-char hex SHA-256 object ID for the 'ours' version.")
214 record_p.add_argument("--theirs-id", required=True, dest="theirs_id",
215 help="64-char hex SHA-256 object ID for the 'theirs' version.")
216 record_p.add_argument("--semantic-fingerprint", dest="semantic_fingerprint", default=None,
217 help="64-char hex semantic fingerprint from a domain plugin. "
218 "Defaults to the blob fingerprint.")
219 record_p.add_argument("--description", default=None,
220 help="JSON-encoded domain-specific metadata (optional).")
221 record_p.add_argument("--recorded-by", dest="recorded_by", default="human",
222 help="Agent ID or 'human' (default: human).")
223 record_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
224 record_p.set_defaults(func=run_record)
225
226 # -- list -----------------------------------------------------------------
227 list_p = subs.add_parser(
228 "list",
229 help="List recorded conflict patterns.",
230 description=(
231 "List all ConflictPatterns in the harmony store, newest first.\n\n"
232 "Agent quickstart\n"
233 "----------------\n"
234 " muse harmony list --json\n"
235 " muse harmony list --domain midi --json\n\n"
236 "JSON output schema\n"
237 "------------------\n"
238 ' {"total": <int>, "patterns": [{...}, ...]}\n\n'
239 "Exit codes\n"
240 "----------\n"
241 " 0 — list returned (may be empty)\n"
242 " 2 — not inside a Muse repository\n"
243 ),
244 formatter_class=argparse.RawDescriptionHelpFormatter,
245 )
246 list_p.add_argument("--domain", default=None,
247 help="Filter to patterns in this domain only.")
248 list_p.add_argument("--conflict-type", dest="conflict_type", default=None,
249 help="Filter to patterns of this conflict type only.")
250 list_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
251 list_p.set_defaults(func=run_list)
252
253 # -- show -----------------------------------------------------------------
254 show_p = subs.add_parser(
255 "show",
256 help="Show a conflict pattern and all its resolutions.",
257 description=(
258 "Display the full ConflictPattern metadata and every saved Resolution,\n"
259 "sorted by quality (human_verified > confidence > applied_count).\n\n"
260 "Agent quickstart\n"
261 "----------------\n"
262 " muse harmony show <pattern_id> --json\n\n"
263 "JSON output schema\n"
264 "------------------\n"
265 ' {"pattern": {...}, "resolutions": [{...}, ...]}\n\n'
266 "Exit codes\n"
267 "----------\n"
268 " 0 — pattern found and shown\n"
269 " 1 — invalid pattern_id or pattern not found\n"
270 " 2 — not inside a Muse repository\n"
271 ),
272 formatter_class=argparse.RawDescriptionHelpFormatter,
273 )
274 show_p.add_argument("pattern_id", metavar="PATTERN_ID",
275 help="64-char hex pattern ID.")
276 show_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
277 show_p.set_defaults(func=run_show)
278
279 # -- resolve --------------------------------------------------------------
280 resolve_p = subs.add_parser(
281 "resolve",
282 help="Save a resolution for a conflict pattern.",
283 description=(
284 "Persist a Resolution to the harmony store. The parent pattern must\n"
285 "already exist (run 'muse harmony record' first). Saving the same\n"
286 "resolution twice is idempotent.\n\n"
287 "Agent quickstart\n"
288 "----------------\n"
289 " muse harmony resolve --pattern-id <hex64> \\\n"
290 " --strategy manual --outcome-blob <hex64> \\\n"
291 " --confidence 0.9 --json\n\n"
292 "JSON output schema\n"
293 "------------------\n"
294 ' {"resolution_id": "<hex64>", "pattern_id": "<hex64>",\n'
295 ' "already_existed": <bool>}\n\n'
296 "Exit codes\n"
297 "----------\n"
298 " 0 — resolution saved (or already existed)\n"
299 " 1 — validation failure, pattern not found, or confidence out of range\n"
300 " 2 — not inside a Muse repository\n"
301 ),
302 formatter_class=argparse.RawDescriptionHelpFormatter,
303 )
304 resolve_p.add_argument("--pattern-id", required=True, dest="pattern_id",
305 help="64-char hex pattern ID to resolve.")
306 resolve_p.add_argument("--strategy", required=True,
307 help="Resolution strategy (manual, exact-replay, "
308 "semantic-proposal, policy).")
309 resolve_p.add_argument("--outcome-blob", required=True, dest="outcome_blob",
310 help="64-char hex SHA-256 of the resolved object.")
311 resolve_p.add_argument("--confidence", required=True, type=float,
312 help="Resolution confidence 0.0–1.0.")
313 resolve_p.add_argument("--rationale", default="",
314 help="Human-readable reasoning for this resolution.")
315 resolve_p.add_argument("--agent-id", dest="agent_id", default=None,
316 help="Agent ID for provenance (omit for human).")
317 resolve_p.add_argument("--model-id", dest="model_id", default=None,
318 help="Model ID for agent provenance.")
319 resolve_p.add_argument("--human-verified", dest="human_verified",
320 action="store_true",
321 help="Mark this resolution as human-verified.")
322 resolve_p.add_argument("--policy-id", dest="policy_id", default=None,
323 help="Policy ID that triggered this resolution (if any).")
324 resolve_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
325 resolve_p.set_defaults(func=run_resolve)
326
327 # -- best -----------------------------------------------------------------
328 best_p = subs.add_parser(
329 "best",
330 help="Show the highest-quality resolution for a pattern.",
331 description=(
332 "Return the best Resolution for a pattern, ranked by\n"
333 "human_verified > confidence > applied_count. Returns null if\n"
334 "no resolutions exist yet.\n\n"
335 "Agent quickstart\n"
336 "----------------\n"
337 " muse harmony best <pattern_id> --json\n\n"
338 "JSON output schema\n"
339 "------------------\n"
340 ' {"pattern_id": "<hex64>", "resolution": {<resolution>|null}}\n\n'
341 "Exit codes\n"
342 "----------\n"
343 " 0 — result returned (resolution may be null)\n"
344 " 1 — invalid pattern_id\n"
345 " 2 — not inside a Muse repository\n"
346 ),
347 formatter_class=argparse.RawDescriptionHelpFormatter,
348 )
349 best_p.add_argument("pattern_id", metavar="PATTERN_ID",
350 help="64-char hex pattern ID.")
351 best_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
352 best_p.set_defaults(func=run_best)
353
354 # -- forget ---------------------------------------------------------------
355 forget_p = subs.add_parser(
356 "forget",
357 help="Remove a conflict pattern and all its resolutions.",
358 description=(
359 "Delete the pattern and every resolution stored under it. Returns\n"
360 "removed=false if the pattern does not exist.\n\n"
361 "Agent quickstart\n"
362 "----------------\n"
363 " muse harmony forget <pattern_id> --json\n\n"
364 "JSON output schema\n"
365 "------------------\n"
366 ' {"pattern_id": "<hex64>", "removed": <bool>}\n\n'
367 "Exit codes\n"
368 "----------\n"
369 " 0 — result returned\n"
370 " 1 — invalid pattern_id\n"
371 " 2 — not inside a Muse repository\n"
372 ),
373 formatter_class=argparse.RawDescriptionHelpFormatter,
374 )
375 forget_p.add_argument("pattern_id", metavar="PATTERN_ID",
376 help="64-char hex pattern ID.")
377 forget_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
378 forget_p.set_defaults(func=run_forget)
379
380 # -- clear ----------------------------------------------------------------
381 clear_p = subs.add_parser(
382 "clear",
383 help="Remove all conflict patterns and their resolutions.",
384 description=(
385 "Delete every pattern entry from the harmony store. This is\n"
386 "irreversible. Pass --yes to skip the confirmation prompt.\n\n"
387 "Agent quickstart\n"
388 "----------------\n"
389 " muse harmony clear --yes --json\n\n"
390 "JSON output schema\n"
391 "------------------\n"
392 ' {"removed": <int>}\n\n'
393 "Exit codes\n"
394 "----------\n"
395 " 0 — clear completed (removed may be 0)\n"
396 " 2 — not inside a Muse repository\n"
397 ),
398 formatter_class=argparse.RawDescriptionHelpFormatter,
399 )
400 clear_p.add_argument("--yes", "-y", action="store_true",
401 help="Skip the confirmation prompt.")
402 clear_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
403 clear_p.set_defaults(func=run_clear)
404
405 # -- gc -------------------------------------------------------------------
406 gc_p = subs.add_parser(
407 "gc",
408 help="Garbage-collect stale unresolved conflict patterns.",
409 description=(
410 "Remove patterns older than --age days that have no saved resolution.\n"
411 "Patterns with at least one resolution are always kept.\n\n"
412 "Agent quickstart\n"
413 "----------------\n"
414 " muse harmony gc --json\n"
415 " muse harmony gc --age 30 --json\n\n"
416 "JSON output schema\n"
417 "------------------\n"
418 ' {"removed": <int>, "age_days": <int>}\n\n'
419 "Exit codes\n"
420 "----------\n"
421 " 0 — gc completed (removed may be 0)\n"
422 " 1 — invalid --age value\n"
423 " 2 — not inside a Muse repository\n"
424 ),
425 formatter_class=argparse.RawDescriptionHelpFormatter,
426 )
427 gc_p.add_argument("--age", type=int, default=90, metavar="DAYS",
428 help="Age threshold in days (default: 90).")
429 gc_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
430 gc_p.set_defaults(func=run_gc)
431
432 # -- policy-add -----------------------------------------------------------
433 padd_p = subs.add_parser(
434 "policy-add",
435 help="Add or replace a declarative resolution policy.",
436 description=(
437 "Persist a Policy to the harmony store. Overwrites any existing\n"
438 "policy with the same policy_id.\n\n"
439 "Agent quickstart\n"
440 "----------------\n"
441 " muse harmony policy-add --policy-id prefer-ours \\\n"
442 " --description 'Always prefer ours for MIDI' \\\n"
443 " --scope domain --action prefer-ours --domain midi --json\n\n"
444 "JSON output schema\n"
445 "------------------\n"
446 ' {"policy_id": "<str>", "action": "<str>", "scope": "<str>"}\n\n'
447 "Exit codes\n"
448 "----------\n"
449 " 0 — policy saved\n"
450 " 1 — invalid policy_id or argument\n"
451 " 2 — not inside a Muse repository\n"
452 ),
453 formatter_class=argparse.RawDescriptionHelpFormatter,
454 )
455 padd_p.add_argument("--policy-id", required=True, dest="policy_id",
456 help="URL-safe alphanumeric policy identifier (1–128 chars).")
457 padd_p.add_argument("--description", required=True,
458 help="Human-readable explanation of what this policy does.")
459 padd_p.add_argument("--scope", required=True,
460 help="Scope: workspace, repo, domain, or file.")
461 padd_p.add_argument("--action", required=True,
462 help="Action: prefer-ours, prefer-theirs, escalate, "
463 "require-human, or delegate.")
464 padd_p.add_argument("--confidence", type=float, default=1.0,
465 help="Confidence 0.0–1.0 assigned to policy-driven resolutions "
466 "(default: 1.0).")
467 padd_p.add_argument("--conflict-type", dest="conflict_type", default=None,
468 help="Filter: only fire for this conflict type.")
469 padd_p.add_argument("--domain", default=None,
470 help="Filter: only fire for this domain.")
471 padd_p.add_argument("--path-pattern", dest="path_pattern", default=None,
472 help="Filter: fnmatch glob for file paths, e.g. '*.mid'.")
473 padd_p.add_argument("--escalate-to", dest="escalate_to", default=None,
474 help="Target for ESCALATE action: 'human' or an agent ID.")
475 padd_p.add_argument("--delegate-to", dest="delegate_to", default=None,
476 help="Agent ID for DELEGATE action.")
477 padd_p.add_argument("--created-by", dest="created_by", default="human",
478 help="Creator attribution (default: human).")
479 padd_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
480 padd_p.set_defaults(func=run_policy_add)
481
482 # -- policy-list ----------------------------------------------------------
483 plist_p = subs.add_parser(
484 "policy-list",
485 help="List all harmony policies, scope-sorted.",
486 description=(
487 "List every Policy from the harmony store. Sorted by scope order\n"
488 "(workspace → repo → domain → file) then by created_at ascending.\n\n"
489 "Agent quickstart\n"
490 "----------------\n"
491 " muse harmony policy-list --json\n\n"
492 "JSON output schema\n"
493 "------------------\n"
494 ' {"total": <int>, "policies": [{...}, ...]}\n\n'
495 "Exit codes\n"
496 "----------\n"
497 " 0 — list returned (may be empty)\n"
498 " 2 — not inside a Muse repository\n"
499 ),
500 formatter_class=argparse.RawDescriptionHelpFormatter,
501 )
502 plist_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
503 plist_p.set_defaults(func=run_policy_list)
504
505 # -- policy-remove --------------------------------------------------------
506 premove_p = subs.add_parser(
507 "policy-remove",
508 help="Remove a harmony policy.",
509 description=(
510 "Delete a policy by ID. Returns removed=false if the policy\n"
511 "does not exist.\n\n"
512 "Agent quickstart\n"
513 "----------------\n"
514 " muse harmony policy-remove <policy_id> --json\n\n"
515 "JSON output schema\n"
516 "------------------\n"
517 ' {"policy_id": "<str>", "removed": <bool>}\n\n'
518 "Exit codes\n"
519 "----------\n"
520 " 0 — result returned\n"
521 " 1 — invalid policy_id\n"
522 " 2 — not inside a Muse repository\n"
523 ),
524 formatter_class=argparse.RawDescriptionHelpFormatter,
525 )
526 premove_p.add_argument("policy_id", metavar="POLICY_ID",
527 help="URL-safe policy identifier.")
528 premove_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
529 premove_p.set_defaults(func=run_policy_remove)
530
531 # -- audit ----------------------------------------------------------------
532 audit_p = subs.add_parser(
533 "audit",
534 help="Show the harmony audit log.",
535 description=(
536 "List recent audit log entries (newest first). The audit log is\n"
537 "append-only — a tamper-evident record of all harmony engine actions.\n\n"
538 "Agent quickstart\n"
539 "----------------\n"
540 " muse harmony audit --json\n"
541 " muse harmony audit --limit 50 --json\n\n"
542 "JSON output schema\n"
543 "------------------\n"
544 ' {"total": <int>, "entries": [{<AuditEvent>}, ...]}\n\n'
545 "Exit codes\n"
546 "----------\n"
547 " 0 — audit log returned (may be empty)\n"
548 " 2 — not inside a Muse repository\n"
549 ),
550 formatter_class=argparse.RawDescriptionHelpFormatter,
551 )
552 audit_p.add_argument("--limit", type=int, default=100, metavar="N",
553 help="Maximum entries to return (default: 100).")
554 audit_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
555 audit_p.set_defaults(func=run_audit)
556
557 # -- engine ---------------------------------------------------------------
558 engine_p = subs.add_parser(
559 "engine",
560 help="Run the three-tier resolution engine for a conflict pattern.",
561 description=(
562 "Evaluate a ConflictPattern through the resolution pipeline:\n"
563 " 1. Policy match — declarative rule fires automatically.\n"
564 " 2. Exact replay — saved resolution above confidence threshold.\n"
565 " 3. Semantic match — similar pattern found via domain plugin.\n"
566 " 4. Escalate — no match; human or specialist-agent required.\n\n"
567 "Agent quickstart\n"
568 "----------------\n"
569 " muse harmony engine <pattern_id> --json\n\n"
570 "JSON output schema\n"
571 "------------------\n"
572 ' {"status": "applied|proposed|escalated",\n'
573 ' "pattern_id": "<hex64>",\n'
574 ' "proposal": {<proposal>|null},\n'
575 ' "applied_resolution_id": "<hex64>|null",\n'
576 ' "escalation_reason": "<str>|null"}\n\n'
577 "Exit codes\n"
578 "----------\n"
579 " 0 — engine ran successfully (check status field)\n"
580 " 1 — invalid pattern_id or invalid threshold\n"
581 " 2 — not inside a Muse repository\n"
582 ),
583 formatter_class=argparse.RawDescriptionHelpFormatter,
584 )
585 engine_p.add_argument("pattern_id", metavar="PATTERN_ID",
586 help="64-char hex pattern ID to resolve.")
587 engine_p.add_argument("--auto-apply-threshold", dest="auto_apply_threshold",
588 type=float, default=None,
589 help="Override auto-apply confidence threshold (0.0–1.0). "
590 "Default: 0.85.")
591 engine_p.add_argument("--auto-escalate", dest="auto_escalate", action="store_true",
592 help="Automatically record an EscalationRecord when the engine "
593 "returns status=escalated.")
594 engine_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
595 engine_p.set_defaults(func=run_engine)
596
597 # -- similar --------------------------------------------------------------
598 similar_p = subs.add_parser(
599 "similar",
600 help="Find conflict patterns semantically similar to the given pattern.",
601 description=(
602 "Search the harmony store for patterns whose semantic fingerprint is\n"
603 "similar to the given pattern. Uses DefaultPlugin (exact fingerprint\n"
604 "match) unless a domain plugin is injected at the engine level.\n\n"
605 "Agent quickstart\n"
606 "----------------\n"
607 " muse harmony similar <pattern_id> --json\n\n"
608 "JSON output schema\n"
609 "------------------\n"
610 ' {"pattern_id": "<hex64>",\n'
611 ' "total": <int>,\n'
612 ' "proposals": [{<proposal>}, ...]}\n\n'
613 "Exit codes\n"
614 "----------\n"
615 " 0 — result returned (proposals may be empty)\n"
616 " 1 — invalid pattern_id\n"
617 " 2 — not inside a Muse repository\n"
618 ),
619 formatter_class=argparse.RawDescriptionHelpFormatter,
620 )
621 similar_p.add_argument("pattern_id", metavar="PATTERN_ID",
622 help="64-char hex pattern ID to find similar patterns for.")
623 similar_p.add_argument("--limit", type=int, default=None, metavar="N",
624 help="Maximum proposals to return (default: engine max_proposals=5).")
625 similar_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
626 similar_p.set_defaults(func=run_similar)
627
628 # -- escalate -------------------------------------------------------------
629 escalate_p = subs.add_parser(
630 "escalate",
631 help="Record an escalation for a conflict pattern requiring human attention.",
632 description=(
633 "Persist an EscalationRecord marking a conflict pattern as open and\n"
634 "awaiting human or specialist-agent resolution. Recording the same\n"
635 "(pattern_id, reason) pair twice is idempotent.\n\n"
636 "Agent quickstart\n"
637 "----------------\n"
638 " muse harmony escalate <pattern_id> --json\n"
639 " muse harmony escalate <pattern_id> --reason 'No policy found' --json\n\n"
640 "JSON output schema\n"
641 "------------------\n"
642 ' {"escalation_id": "<hex64>", "pattern_id": "<hex64>",\n'
643 ' "already_existed": <bool>}\n\n'
644 "Exit codes\n"
645 "----------\n"
646 " 0 — escalation recorded (or already existed)\n"
647 " 1 — invalid pattern_id\n"
648 " 2 — not inside a Muse repository\n"
649 ),
650 formatter_class=argparse.RawDescriptionHelpFormatter,
651 )
652 escalate_p.add_argument("pattern_id", metavar="PATTERN_ID",
653 help="64-char hex pattern ID to escalate.")
654 escalate_p.add_argument("--reason", default="Escalated for human review",
655 help="Human-readable reason for escalation.")
656 escalate_p.add_argument("--agent-id", dest="agent_id", default=None,
657 help="Agent ID for provenance (omit for human).")
658 escalate_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
659 escalate_p.set_defaults(func=run_escalate)
660
661 # -- escalations ----------------------------------------------------------
662 escalations_p = subs.add_parser(
663 "escalations",
664 help="List conflict pattern escalations (operator dashboard).",
665 description=(
666 "List EscalationRecords — the operator dashboard of unresolved conflicts.\n"
667 "Filter by --status to see open, resolved, or all escalations.\n\n"
668 "Agent quickstart\n"
669 "----------------\n"
670 " muse harmony escalations --json\n"
671 " muse harmony escalations --status open --json\n\n"
672 "JSON output schema\n"
673 "------------------\n"
674 ' {"total": <int>, "escalations": [{...}, ...]}\n\n'
675 "Exit codes\n"
676 "----------\n"
677 " 0 — list returned (may be empty)\n"
678 " 2 — not inside a Muse repository\n"
679 ),
680 formatter_class=argparse.RawDescriptionHelpFormatter,
681 )
682 escalations_p.add_argument("--status", default=None,
683 choices=["open", "resolved"],
684 help="Filter by escalation status.")
685 escalations_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
686 escalations_p.set_defaults(func=run_escalations)
687
688 # -- resolve-escalation ---------------------------------------------------
689 resolve_esc_p = subs.add_parser(
690 "resolve-escalation",
691 help="Close an escalation after a conflict is manually resolved.",
692 description=(
693 "Transition an EscalationRecord from OPEN to RESOLVED. Links the\n"
694 "escalation to the resolution that closed it. Returns resolved=false\n"
695 "if the escalation does not exist.\n\n"
696 "Agent quickstart\n"
697 "----------------\n"
698 " muse harmony resolve-escalation <esc_id> --resolution-id <hex64> --json\n\n"
699 "JSON output schema\n"
700 "------------------\n"
701 ' {"escalation_id": "<hex64>", "resolved": <bool>}\n\n'
702 "Exit codes\n"
703 "----------\n"
704 " 0 — result returned (resolved may be false)\n"
705 " 1 — invalid escalation_id or resolution_id\n"
706 " 2 — not inside a Muse repository\n"
707 ),
708 formatter_class=argparse.RawDescriptionHelpFormatter,
709 )
710 resolve_esc_p.add_argument("escalation_id", metavar="ESCALATION_ID",
711 help="64-char hex escalation ID to close.")
712 resolve_esc_p.add_argument("--resolution-id", required=True, dest="resolution_id",
713 help="64-char hex resolution ID that closes this escalation.")
714 resolve_esc_p.add_argument("--agent-id", dest="agent_id", default=None,
715 help="Agent ID for resolved_by provenance (omit for human).")
716 resolve_esc_p.add_argument("--json", "-j", action="store_true", dest="json_out", help="Emit JSON output.")
717 resolve_esc_p.set_defaults(func=run_resolve_escalation)
File History 9 commits
sha256:37af4ab271a64f11d7bf14b4871ebaaf5dd68fb0247521007c6b034d35aa01be Merge branch 'fix/hub-user-read-update-path' into dev Human 10 days ago
sha256:b7be56ec091919a612cffe7f3c8b600209d5155517443fdac0e16954c8c7d81f Merge 'task/git-export-ignored-file-deletion' into 'dev' — … Human 12 days ago
sha256:8de4334a98c945aace420969d389ad678aa926d4ab4e886b2ac4c4241cb3bf2b revert: keep pyproject.toml in canonical PEP 440 form Sonnet 4.6 patch 16 days ago
sha256:50bb615c573aa4b928fca75b60f82ba2a659910066507cec6a95c412ae22ccb9 docs: add issue docs for push have-negotiation bug (#55) an… Sonnet 4.6 19 days ago
sha256:d90d175cded68aae1d4ffcf4858917854195d0cd8ce1fe73cee4dbc02541cb74 chore: trigger push to surface null-OID paths Sonnet 4.6 25 days ago
sha256:e452ad9a6ace6ccc6d875a35e06caf9da5576a970c1c36133b69a891ce5fefa8 chore: prebuild timing test Sonnet 4.6 35 days ago
sha256:0008ab6695e3e064b3e236b24fd19e538fef6a588eb0d211622f4466d919c0b1 merge: pull staging/dev — advance to 0.2.0rc12 Sonnet 4.6 patch 37 days ago
sha256:9c33d61749fff814c5226d5386aa2af7064c2c02788594a25fdd709358132eea fix: _PROPOSAL_PREFIX_RESOLVE_LIMIT 200 → 100 to match hub … Sonnet 4.6 48 days ago
sha256:36c3cb3e76619d4c30a6d9bf81b5ec4ff148e30dcfed913e3114ca7b43b81c7e fix: rename objects→blobs in push client and all stale test… Sonnet 4.6 patch 51 days ago