@andresmassello/uscha 1.51.1 → 1.53.0
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 +24 -5
- package/package.json +3 -2
- package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +203 -161
- package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +40 -0
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +40 -0
- package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +203 -161
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +192 -161
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +16 -8
- package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +42 -0
- package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +119 -79
- package/uscha-kit/.claude/skills/uscha-status/SKILL.md +24 -0
- package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +128 -88
- package/uscha-kit/.claude-plugin/plugin.json +2 -2
- package/uscha-kit/.codex-plugin/plugin.json +2 -2
- package/uscha-kit/INSTALL.md +3 -0
- package/uscha-kit/README.md +1 -1
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/install-uscha.py +77 -43
- package/uscha-kit/skills/uscha-adr-refine/SKILL.md +203 -161
- package/uscha-kit/skills/uscha-characterize/SKILL.md +40 -0
- package/uscha-kit/skills/uscha-devloop/SKILL.md +40 -0
- package/uscha-kit/skills/uscha-discovery/SKILL.md +203 -161
- package/uscha-kit/skills/uscha-mirador/SKILL.md +192 -161
- package/uscha-kit/skills/uscha-mirador/mirador-render.py +16 -8
- package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +42 -0
- package/uscha-kit/skills/uscha-rubric/SKILL.md +119 -79
- package/uscha-kit/skills/uscha-status/SKILL.md +24 -0
- package/uscha-kit/skills/uscha-sysdoc/SKILL.md +128 -88
- package/uscha-kit/uscha.config.json +1 -1
- package/uscha-kit/CHANGELOG-1.10.0.md +0 -84
- package/uscha-kit/CHANGELOG-1.11.0.md +0 -67
- package/uscha-kit/CHANGELOG-1.12.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.13.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.14.0.md +0 -42
- package/uscha-kit/CHANGELOG-1.15.0.md +0 -58
- package/uscha-kit/CHANGELOG-1.16.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.17.0.md +0 -44
- package/uscha-kit/CHANGELOG-1.18.0.md +0 -42
- package/uscha-kit/CHANGELOG-1.19.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.2.2.md +0 -16
- package/uscha-kit/CHANGELOG-1.2.3.md +0 -20
- package/uscha-kit/CHANGELOG-1.2.4.md +0 -10
- package/uscha-kit/CHANGELOG-1.2.5.md +0 -23
- package/uscha-kit/CHANGELOG-1.2.6.md +0 -11
- package/uscha-kit/CHANGELOG-1.2.7.md +0 -15
- package/uscha-kit/CHANGELOG-1.2.8.md +0 -24
- package/uscha-kit/CHANGELOG-1.2.9.md +0 -4
- package/uscha-kit/CHANGELOG-1.20.0.md +0 -29
- package/uscha-kit/CHANGELOG-1.21.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.22.0.md +0 -60
- package/uscha-kit/CHANGELOG-1.23.0.md +0 -75
- package/uscha-kit/CHANGELOG-1.24.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.25.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.26.0.md +0 -70
- package/uscha-kit/CHANGELOG-1.27.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.28.0.md +0 -35
- package/uscha-kit/CHANGELOG-1.29.0.md +0 -20
- package/uscha-kit/CHANGELOG-1.3.0.md +0 -74
- package/uscha-kit/CHANGELOG-1.30.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.31.0.md +0 -59
- package/uscha-kit/CHANGELOG-1.32.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.33.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.34.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.35.0.md +0 -30
- package/uscha-kit/CHANGELOG-1.36.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.37.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.38.0.md +0 -11
- package/uscha-kit/CHANGELOG-1.39.0.md +0 -14
- package/uscha-kit/CHANGELOG-1.4.0.md +0 -68
- package/uscha-kit/CHANGELOG-1.40.0.md +0 -16
- package/uscha-kit/CHANGELOG-1.40.1.md +0 -11
- package/uscha-kit/CHANGELOG-1.40.2.md +0 -13
- package/uscha-kit/CHANGELOG-1.41.0.md +0 -18
- package/uscha-kit/CHANGELOG-1.41.1.md +0 -53
- package/uscha-kit/CHANGELOG-1.41.2.md +0 -34
- package/uscha-kit/CHANGELOG-1.41.3.md +0 -30
- package/uscha-kit/CHANGELOG-1.42.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.43.0.md +0 -37
- package/uscha-kit/CHANGELOG-1.44.0.md +0 -90
- package/uscha-kit/CHANGELOG-1.44.1.md +0 -26
- package/uscha-kit/CHANGELOG-1.45.0.md +0 -58
- package/uscha-kit/CHANGELOG-1.46.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.46.1.md +0 -35
- package/uscha-kit/CHANGELOG-1.47.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.48.0.md +0 -35
- package/uscha-kit/CHANGELOG-1.48.1.md +0 -55
- package/uscha-kit/CHANGELOG-1.48.2.md +0 -47
- package/uscha-kit/CHANGELOG-1.49.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.5.0.md +0 -64
- package/uscha-kit/CHANGELOG-1.50.0.md +0 -52
- package/uscha-kit/CHANGELOG-1.50.1.md +0 -52
- package/uscha-kit/CHANGELOG-1.50.2.md +0 -62
- package/uscha-kit/CHANGELOG-1.51.0.md +0 -44
- package/uscha-kit/CHANGELOG-1.51.1.md +0 -33
- package/uscha-kit/CHANGELOG-1.6.0.md +0 -57
- package/uscha-kit/CHANGELOG-1.7.0.md +0 -74
- package/uscha-kit/CHANGELOG-1.8.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.9.0.md +0 -112
|
@@ -20,7 +20,18 @@ PLUGIN_NAME = "uscha"
|
|
|
20
20
|
SKILLS = ["uscha-discovery", "uscha-adr-refine", "uscha-reverse-discovery", "uscha-characterize",
|
|
21
21
|
"uscha-devloop", "uscha-sysdoc", "uscha-rubric", "uscha-mirador",
|
|
22
22
|
"uscha-status"]
|
|
23
|
-
|
|
23
|
+
# Agent-Skills targets: harness-neutral, SKILLS-ONLY installs. Each agent reads the 9 uscha-*
|
|
24
|
+
# skill directories from its own root -- no plugin manifest, no settings.json, no hook. They all
|
|
25
|
+
# share one transactional installer and one doctor branch, so a sixth costs a table row (kit
|
|
26
|
+
# 1.53.0). Roots are the directories each agent documents for the Agent Skills standard.
|
|
27
|
+
SKILL_ROOTS = {
|
|
28
|
+
"pi": (".agents", "skills"), # Earendil pi
|
|
29
|
+
"cursor": (".cursor", "skills"), # Cursor
|
|
30
|
+
"copilot": (".copilot", "skills"), # VS Code / GitHub Copilot
|
|
31
|
+
"gemini": (".gemini", "skills"), # Gemini CLI
|
|
32
|
+
"cline": (".cline", "skills"), # Cline
|
|
33
|
+
}
|
|
34
|
+
TARGETS = ("codex", "claude") + tuple(SKILL_ROOTS)
|
|
24
35
|
HOOK_NAME = "block-approved-writes.py"
|
|
25
36
|
|
|
26
37
|
|
|
@@ -41,8 +52,9 @@ def home_path(args):
|
|
|
41
52
|
|
|
42
53
|
|
|
43
54
|
def selected_targets(value):
|
|
44
|
-
# `all` = every target
|
|
45
|
-
# codex+claude so existing scripts/users keep their exact
|
|
55
|
+
# `all` = every target in TARGETS, so a new Agent-Skills row is picked up automatically;
|
|
56
|
+
# `both` stays a LEGACY alias for codex+claude so existing scripts/users keep their exact
|
|
57
|
+
# prior behavior -- it deliberately does NOT grow as targets are added.
|
|
46
58
|
return {"all": list(TARGETS), "both": ["codex", "claude"]}.get(value, [value])
|
|
47
59
|
|
|
48
60
|
|
|
@@ -115,7 +127,7 @@ def plugin_manifest():
|
|
|
115
127
|
return {"name": PLUGIN_NAME, "version": source_version(),
|
|
116
128
|
"description": "Uscha spec-driven development methodology for coding agents.",
|
|
117
129
|
"author": {"name": "Andres Massello", "url": "https://github.com/andresmassello"},
|
|
118
|
-
"homepage": "https://
|
|
130
|
+
"homepage": "https://uscha.dev", "repository": "https://github.com/andresmassello/uscha",
|
|
119
131
|
"license": "MIT", "keywords": ["spec-driven", "qa", "gates", "golden-testing", "readiness"],
|
|
120
132
|
"skills": "./skills/", "interface": {"displayName": "Uscha", "shortDescription": "Spec-driven development with fact gates and readiness.",
|
|
121
133
|
"longDescription": "Uscha installs discovery, ADR, characterization, devloop, rubric, sysdoc and Mirador skills plus qa_ledger.py.",
|
|
@@ -434,15 +446,15 @@ def install_claude(home, mode, dry_run, operations):
|
|
|
434
446
|
pass
|
|
435
447
|
return root
|
|
436
448
|
|
|
437
|
-
def
|
|
438
|
-
#
|
|
439
|
-
#
|
|
440
|
-
#
|
|
441
|
-
#
|
|
449
|
+
def install_skills_only(target, home, mode, dry_run, operations):
|
|
450
|
+
# One installer for every Agent-Skills target (SKILL_ROOTS): the 9 skills land flat under
|
|
451
|
+
# that agent's own root. Skills only -- no manifest, no settings.json, no hook. Same
|
|
452
|
+
# transactional shape as install_claude: stage -> back up -> atomic replace -> marker last,
|
|
453
|
+
# so a late failure rolls the target back with nothing lost.
|
|
442
454
|
source = source_skills()
|
|
443
|
-
root = home
|
|
455
|
+
root = home.joinpath(*SKILL_ROOTS[target])
|
|
444
456
|
install_marker = root / "uscha-install.json"
|
|
445
|
-
marker_data = marker(
|
|
457
|
+
marker_data = marker(target, root, mode)
|
|
446
458
|
operations.extend({"action": "install-skill", "path": str(root / skill)} for skill in SKILLS)
|
|
447
459
|
operations.append({"action": "write-marker-last", "path": str(install_marker)})
|
|
448
460
|
if dry_run:
|
|
@@ -450,7 +462,7 @@ def install_pi(home, mode, dry_run, operations):
|
|
|
450
462
|
|
|
451
463
|
home_existed = home.exists()
|
|
452
464
|
home.mkdir(parents=True, exist_ok=True)
|
|
453
|
-
transaction = home / (".uscha-
|
|
465
|
+
transaction = home / (".uscha-%s-transaction-%s" % (target, uuid.uuid4().hex))
|
|
454
466
|
staged = transaction / "staged"
|
|
455
467
|
backups = transaction / "backups"
|
|
456
468
|
cleanup_transaction = True
|
|
@@ -463,34 +475,38 @@ def install_pi(home, mode, dry_run, operations):
|
|
|
463
475
|
|
|
464
476
|
entries = [(root / skill, staged / skill, backups / skill) for skill in SKILLS]
|
|
465
477
|
entries.append((install_marker, staged / "uscha-install.json", backups / "uscha-install.json"))
|
|
466
|
-
|
|
467
|
-
|
|
478
|
+
# The loops below bind PATHS. They must NOT be named `target`: a Python for-loop has no
|
|
479
|
+
# scope of its own, so that would permanently rebind this function's `target` argument
|
|
480
|
+
# (the target NAME) to a Path, and the rollback error below would then report a file
|
|
481
|
+
# path instead of naming which target failed -- exactly when that matters most.
|
|
482
|
+
preexisting = {path: path.exists() or path.is_symlink()
|
|
483
|
+
for path, _, _ in entries}
|
|
468
484
|
created_dirs = []
|
|
469
485
|
backed_up = set()
|
|
470
486
|
installed = set()
|
|
471
487
|
try:
|
|
472
|
-
for directory in (root.parent, root): # ~/.
|
|
488
|
+
for directory in (root.parent, root): # e.g. ~/.cursor then ~/.cursor/skills
|
|
473
489
|
if not directory.exists():
|
|
474
490
|
directory.mkdir(parents=True)
|
|
475
491
|
created_dirs.append(directory)
|
|
476
|
-
for
|
|
477
|
-
if preexisting[
|
|
492
|
+
for path, replacement, backup in entries:
|
|
493
|
+
if preexisting[path]:
|
|
478
494
|
backup.parent.mkdir(parents=True, exist_ok=True)
|
|
479
|
-
os.replace(
|
|
480
|
-
backed_up.add(
|
|
481
|
-
os.replace(replacement,
|
|
482
|
-
installed.add(
|
|
495
|
+
os.replace(path, backup)
|
|
496
|
+
backed_up.add(path)
|
|
497
|
+
os.replace(replacement, path)
|
|
498
|
+
installed.add(path)
|
|
483
499
|
except Exception as exc:
|
|
484
500
|
rollback_errors = []
|
|
485
|
-
for
|
|
501
|
+
for path, _, backup in reversed(entries):
|
|
486
502
|
try:
|
|
487
|
-
if
|
|
488
|
-
remove_path(
|
|
489
|
-
if
|
|
490
|
-
|
|
491
|
-
os.replace(backup,
|
|
503
|
+
if path in installed and (path.exists() or path.is_symlink()):
|
|
504
|
+
remove_path(path)
|
|
505
|
+
if path in backed_up and (backup.exists() or backup.is_symlink()):
|
|
506
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
507
|
+
os.replace(backup, path)
|
|
492
508
|
except Exception as rollback_exc:
|
|
493
|
-
rollback_errors.append("%s: %s" % (
|
|
509
|
+
rollback_errors.append("%s: %s" % (path, rollback_exc))
|
|
494
510
|
for directory in reversed(created_dirs):
|
|
495
511
|
try:
|
|
496
512
|
directory.rmdir()
|
|
@@ -499,8 +515,8 @@ def install_pi(home, mode, dry_run, operations):
|
|
|
499
515
|
if rollback_errors:
|
|
500
516
|
cleanup_transaction = False
|
|
501
517
|
raise InstallError(
|
|
502
|
-
"[install-uscha]
|
|
503
|
-
% (transaction, "; ".join(rollback_errors))
|
|
518
|
+
"[install-uscha] %s rollback incomplete; recovery retained at %s (%s)"
|
|
519
|
+
% (target, transaction, "; ".join(rollback_errors))
|
|
504
520
|
) from exc
|
|
505
521
|
raise
|
|
506
522
|
finally:
|
|
@@ -542,11 +558,13 @@ def target_status(home, target):
|
|
|
542
558
|
marketplace_ok = False
|
|
543
559
|
checks = {"skills_present": skills_present, "manifest": manifest_ok, "marketplace_registered": marketplace_ok}
|
|
544
560
|
guard = "advisory" # Codex has no hooks mechanism (verified: .codex-plugin has no "hooks")
|
|
545
|
-
elif target
|
|
546
|
-
#
|
|
547
|
-
#
|
|
548
|
-
#
|
|
549
|
-
|
|
561
|
+
elif target in SKILL_ROOTS:
|
|
562
|
+
# Agent-Skills targets: the skills live FLAT under that agent's root; no manifest, no
|
|
563
|
+
# hook. INV-GOLDEN-01 is therefore ADVISORY on all of them -- none exposes a blocking
|
|
564
|
+
# pre-tool hook the way Claude's PreToolUse does. pi is the one exception in waiting: a
|
|
565
|
+
# `tool_call` extension ships (uscha-kit/pi/golden-guard.js), but it stays advisory until
|
|
566
|
+
# a real pi run measures the block. The kit does not claim enforcement it has not seen.
|
|
567
|
+
root = home.joinpath(*SKILL_ROOTS[target])
|
|
550
568
|
skills_root, marker_path = root, root / "uscha-install.json"
|
|
551
569
|
skills_present = [skill for skill in SKILLS if (skills_root / skill / "SKILL.md").is_file()]
|
|
552
570
|
checks = {"skills_present": skills_present}
|
|
@@ -579,9 +597,13 @@ def cmd_version(args):
|
|
|
579
597
|
|
|
580
598
|
def cmd_install(args):
|
|
581
599
|
home, operations, installed = home_path(args), [], {}
|
|
582
|
-
installers = {"codex": install_codex, "claude": install_claude
|
|
600
|
+
installers = {"codex": install_codex, "claude": install_claude}
|
|
583
601
|
for target in selected_targets(args.target):
|
|
584
|
-
|
|
602
|
+
if target in SKILL_ROOTS:
|
|
603
|
+
root = install_skills_only(target, home, args.mode, args.dry_run, operations)
|
|
604
|
+
else:
|
|
605
|
+
root = installers[target](home, args.mode, args.dry_run, operations)
|
|
606
|
+
installed[target] = str(root)
|
|
585
607
|
emit({"status": "planned" if args.dry_run else "installed", "dry_run": args.dry_run, "source_version": source_version(), "home": str(home), "installed": installed, "operations": operations, "next": next_steps(args.target)}, args.json)
|
|
586
608
|
|
|
587
609
|
|
|
@@ -760,8 +782,11 @@ def cmd_mirador(args):
|
|
|
760
782
|
raise SystemExit(1)
|
|
761
783
|
base = [sys.executable, str(render), "--ledger", args.ledger, "--out", args.out]
|
|
762
784
|
if not args.watch:
|
|
763
|
-
# one-shot: the renderer writes the file and opens it
|
|
764
|
-
|
|
785
|
+
# one-shot: the renderer writes the file and opens it only when FIRST materializing it
|
|
786
|
+
# -- a re-render updates the already-open tab in place instead of spawning another
|
|
787
|
+
# (kit 1.51.2). --no-open suppresses; --open forces a reopen when the tab was closed.
|
|
788
|
+
extra = ["--no-open"] if args.no_open else (["--open"] if args.force_open else [])
|
|
789
|
+
rc = subprocess.call(base + extra)
|
|
765
790
|
if rc:
|
|
766
791
|
raise SystemExit(rc)
|
|
767
792
|
return
|
|
@@ -787,8 +812,15 @@ def next_steps(target):
|
|
|
787
812
|
steps = []
|
|
788
813
|
if "codex" in picked: steps.append("Codex: restart or open a new thread, then install/use uscha from the Personal marketplace if needed.")
|
|
789
814
|
if "claude" in picked: steps.append("Claude: restart Claude Code so global skills/hooks are reloaded.")
|
|
790
|
-
|
|
791
|
-
|
|
815
|
+
labels = {"pi": "pi (Earendil)", "cursor": "Cursor", "copilot": "VS Code / GitHub Copilot",
|
|
816
|
+
"gemini": "Gemini CLI", "cline": "Cline"}
|
|
817
|
+
for name in SKILL_ROOTS:
|
|
818
|
+
if name in picked:
|
|
819
|
+
steps.append("%s: restart it; the 9 uscha-* skills load from ~/%s. INV-GOLDEN-01 is "
|
|
820
|
+
"advisory there (no blocking pre-tool hook) -- doctor reports golden_guard."
|
|
821
|
+
% (labels.get(name, name), "/".join(SKILL_ROOTS[name])))
|
|
822
|
+
return steps + ["Run: python install-uscha.py doctor --target %s" % target,
|
|
823
|
+
"Learn the method: https://uscha.dev"]
|
|
792
824
|
|
|
793
825
|
|
|
794
826
|
def emit(data, as_json):
|
|
@@ -809,9 +841,9 @@ def build_parser():
|
|
|
809
841
|
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
810
842
|
version = sub.add_parser("version", help="show source version and supported targets"); version.add_argument("--json", action="store_true"); version.set_defaults(func=cmd_version)
|
|
811
843
|
install = sub.add_parser("install", help="install Uscha globally for a machine")
|
|
812
|
-
install.add_argument("--target", choices=
|
|
844
|
+
install.add_argument("--target", choices=list(TARGETS) + ["both", "all"], default="both"); install.add_argument("--mode", choices=["copy", "link"], default="copy"); install.add_argument("--home"); install.add_argument("--dry-run", action="store_true"); install.add_argument("--json", action="store_true"); install.set_defaults(func=cmd_install)
|
|
813
845
|
doctor = sub.add_parser("doctor", help="check installed Uscha presence, registrations, and version drift")
|
|
814
|
-
doctor.add_argument("--target", choices=
|
|
846
|
+
doctor.add_argument("--target", choices=list(TARGETS) + ["both", "all"], default="both"); doctor.add_argument("--home"); doctor.add_argument("--json", action="store_true"); doctor.set_defaults(func=cmd_doctor)
|
|
815
847
|
init = sub.add_parser("init", help="prepare a repo with Uscha config/templates")
|
|
816
848
|
init.add_argument("--repo", default="."); init.add_argument("--force", action="store_true", help="replace differing init files deliberately"); init.add_argument("--dry-run", action="store_true"); init.add_argument("--json", action="store_true"); init.set_defaults(func=cmd_init)
|
|
817
849
|
mirador = sub.add_parser("mirador", help="render + open the project's mirador dashboard from QA-LEDGER.json")
|
|
@@ -820,6 +852,8 @@ def build_parser():
|
|
|
820
852
|
mirador.add_argument("--watch", action="store_true", help="live second-screen view: re-render every --interval seconds")
|
|
821
853
|
mirador.add_argument("--interval", type=int, default=30)
|
|
822
854
|
mirador.add_argument("--no-open", action="store_true", help="write the file but do not open a browser")
|
|
855
|
+
mirador.add_argument("--open", dest="force_open", action="store_true",
|
|
856
|
+
help="open the browser even if mirador.html already existed (you closed the tab)")
|
|
823
857
|
mirador.set_defaults(func=cmd_mirador)
|
|
824
858
|
return parser
|
|
825
859
|
|
|
@@ -1,161 +1,203 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: uscha-adr-refine
|
|
3
|
-
description: >
|
|
4
|
-
Turn a rough idea into a development-ready ADR set + ACCEPTANCE.md by INTERROGATING
|
|
5
|
-
before generating. Runs a structured Socratic interview (problem, implicit decisions,
|
|
6
|
-
behavior incl. failure modes, inviolable constraints, out-of-scope, Definition of
|
|
7
|
-
Done, dependencies), refuses to emit artifacts until the gaps are closed, then
|
|
8
|
-
distills the conversation into docs/adr/ADR-NNN.md files and an ACCEPTANCE.md. The
|
|
9
|
-
front-half counterpart to dev-loop. Invoke for "refine the ADR", "let's spec this
|
|
10
|
-
before coding", "ayudame a definir esto antes de desarrollar".
|
|
11
|
-
allowed-tools: Read, Write, Glob, Grep
|
|
12
|
-
disable-model-invocation: false
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
# adr-refine — interview, then distill
|
|
16
|
-
|
|
17
|
-
You convert a rough idea into a development-ready specification. You do this in two
|
|
18
|
-
phases. **You are NOT a generator. You are an interrogator that distills.** The value
|
|
19
|
-
is in the questions, not in agreeing.
|
|
20
|
-
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
##
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
1
|
+
---
|
|
2
|
+
name: uscha-adr-refine
|
|
3
|
+
description: >
|
|
4
|
+
Turn a rough idea into a development-ready ADR set + ACCEPTANCE.md by INTERROGATING
|
|
5
|
+
before generating. Runs a structured Socratic interview (problem, implicit decisions,
|
|
6
|
+
behavior incl. failure modes, inviolable constraints, out-of-scope, Definition of
|
|
7
|
+
Done, dependencies), refuses to emit artifacts until the gaps are closed, then
|
|
8
|
+
distills the conversation into docs/adr/ADR-NNN.md files and an ACCEPTANCE.md. The
|
|
9
|
+
front-half counterpart to dev-loop. Invoke for "refine the ADR", "let's spec this
|
|
10
|
+
before coding", "ayudame a definir esto antes de desarrollar".
|
|
11
|
+
allowed-tools: Read, Write, Glob, Grep
|
|
12
|
+
disable-model-invocation: false
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# adr-refine — interview, then distill
|
|
16
|
+
|
|
17
|
+
You convert a rough idea into a development-ready specification. You do this in two
|
|
18
|
+
phases. **You are NOT a generator. You are an interrogator that distills.** The value
|
|
19
|
+
is in the questions, not in agreeing.
|
|
20
|
+
|
|
21
|
+
## Orientation markers (non-negotiable)
|
|
22
|
+
|
|
23
|
+
The operator must never have to ask "where am I?" or "what happens now?". Two markers, always.
|
|
24
|
+
They are navigation, not ceremony: one line per turn, one block at the end.
|
|
25
|
+
|
|
26
|
+
**Open every turn with a breadcrumb**, then the content:
|
|
27
|
+
|
|
28
|
+
`[uscha · adr-refine · <step> → <target>]`
|
|
29
|
+
|
|
30
|
+
- `<step>` — `Q<n>` for a question, `pass <n>` for a loop iteration, `step <n>` otherwise.
|
|
31
|
+
Count what has actually happened. **Never write a denominator** (`Q4/12`): this phase
|
|
32
|
+
converges, its length is not known in advance, and an invented total is exactly the kind of
|
|
33
|
+
narrated number the method forbids. **When the ledger already measures the count** (the QA
|
|
34
|
+
loop's `loop_count`), use the measured number — never keep a parallel tally of your own.
|
|
35
|
+
- `<target>` — the artifact this turn feeds (`SPEC`, `ADR-003`, `ACCEPTANCE`, `LEDGER`,
|
|
36
|
+
`RECEIVED`, ...). Drop `→ <target>` only when the turn genuinely feeds none.
|
|
37
|
+
|
|
38
|
+
**Close with the close block ONCE, when the skill finishes** — not on every turn. Ending
|
|
39
|
+
without it is a defect, even when the phase converged cleanly:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
[uscha · adr-refine · CLOSED]
|
|
43
|
+
Produced: <files actually written, or "nothing">
|
|
44
|
+
Blocks: <what stands between here and the next phase, or "nothing">
|
|
45
|
+
Next: <the next action, and why it is that one>
|
|
46
|
+
Run: <the exact command or skill to invoke>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This is **not** the implementation handoff some skills also emit: that one is a prompt for
|
|
50
|
+
whoever implements next, this one is navigation for the human operator, and both can appear.
|
|
51
|
+
|
|
52
|
+
`Blocks` and `Next` are **derived from the state you just produced** — never copied from a
|
|
53
|
+
fixed route, **including any `Flow:` line in this file**. Those lines are the nominal path;
|
|
54
|
+
open ADR experiments, an unclosed spike, an unapproved golden or a red gate all change what
|
|
55
|
+
genuinely comes next, and the derived answer wins. If the next phase cannot start yet, name it
|
|
56
|
+
and say exactly what unblocks it.
|
|
57
|
+
|
|
58
|
+
Keep the CONTENT in the conversation's language, but keep the labels (`CLOSED`, `Produced`,
|
|
59
|
+
`Blocks`, `Next`, `Run`) verbatim — they are the method's vocabulary and the smoke checks them.
|
|
60
|
+
|
|
61
|
+
## Non-negotiable principles
|
|
62
|
+
|
|
63
|
+
1. **Interrogate, don't agree.** Your job in Phase A is to surface what the human left
|
|
64
|
+
implicit and to find the holes — not to validate. A refinement where you agreed with
|
|
65
|
+
everything failed.
|
|
66
|
+
2. **Converge, don't run out of questions.** The interview ends on an objective
|
|
67
|
+
criterion (below), not when the human seems tired or you run out of ideas. This
|
|
68
|
+
mirrors dev-loop's "converge, don't chase zero" — the same discipline at the front.
|
|
69
|
+
3. **Do not emit artifacts until convergence.** No ADR, no ACCEPTANCE.md until every
|
|
70
|
+
exit condition is met. If asked to "just write it" early, name the open gaps first.
|
|
71
|
+
4. **One topic at a time.** Never dump 20 questions. Walk the agenda below, a focused
|
|
72
|
+
batch at a time, and reflect back what you heard before moving on.
|
|
73
|
+
5. **Record deferrals as explicit assumptions.** If the human says "you decide" on a
|
|
74
|
+
consequential decision, push back once with the trade-off; if they still defer,
|
|
75
|
+
record it as an explicit assumption in the ADR, never as a silent default.
|
|
76
|
+
|
|
77
|
+
## Phase A — The interview (agenda)
|
|
78
|
+
|
|
79
|
+
Start from the human's initial context. Work the agenda in order; skip a topic only if
|
|
80
|
+
it's already fully answered. Keep a running list of OPEN GAPS and resolved decisions.
|
|
81
|
+
|
|
82
|
+
1. **Problem and why now.** What job does this remove? What does it cost to NOT do
|
|
83
|
+
it (money, time, risk)? If "why now" has no answer, the priority is suspect.
|
|
84
|
+
2. **Implicit decisions.** Surface the choices the request assumed: sync vs async,
|
|
85
|
+
storage, protocol, idempotency, transactional boundaries, who owns state. For each,
|
|
86
|
+
force an explicit decision and at least one considered alternative.
|
|
87
|
+
3. **Behavior.** Happy path first, then the DIRTY cases: provider/timeout failures,
|
|
88
|
+
retries and backoff, 4xx vs 5xx, concurrency, partial/terminal states, what must NOT
|
|
89
|
+
happen. A feature without its failure behavior is half-specified.
|
|
90
|
+
4. **Inviolable constraints (→ `CONSTITUTION.md`).** Domain + security + operation
|
|
91
|
+
rules that cannot be broken (money to the cent, numbering without gaps, never cross
|
|
92
|
+
environments/credentials, secrets never logged, auth/authz, data retention). Write/extend
|
|
93
|
+
`CONSTITUTION.md` (one invariant per line, CWE ref where it maps); these feed the
|
|
94
|
+
dev-loop severity gate and a breach is a BLOCKER. **An ADR may never contradict the
|
|
95
|
+
CONSTITUTION** — if a decision would, it's escalated, not recorded.
|
|
96
|
+
5. **Out of scope.** Explicit boundaries, with forward references ("X goes to a later
|
|
97
|
+
spec"). What you exclude is as important as what you include.
|
|
98
|
+
6. **Definition of Done + how we measure success.** Concrete, checkable acceptance criteria
|
|
99
|
+
(tests green, documented, metrics published, runbook) AND success metrics (p95,
|
|
100
|
+
cost ceiling, zero orphaned records). Each item must be verifiable, not a feeling.
|
|
101
|
+
7. **Dependencies.** Which other specs/systems/credentials this needs to exist first.
|
|
102
|
+
|
|
103
|
+
After each batch, reflect: "Decided: … / Still open: …". Move on only when the
|
|
104
|
+
current topic is closed.
|
|
105
|
+
|
|
106
|
+
## Convergence — exit conditions (ALL must hold)
|
|
107
|
+
|
|
108
|
+
- Every decision has a rationale and at least one considered alternative.
|
|
109
|
+
- Every failure mode named has a defined behavior.
|
|
110
|
+
- Out-of-scope is explicit.
|
|
111
|
+
- The Definition of Done exists and every item is checkable.
|
|
112
|
+
- No OPEN GAP you raised remains unresolved (resolved = decided OR recorded as an
|
|
113
|
+
explicit assumption).
|
|
114
|
+
|
|
115
|
+
State plainly when you've converged ("Closed: every decision has a rationale, the failures
|
|
116
|
+
have behavior, the scope has a boundary and the DoD is verifiable.") before Phase B.
|
|
117
|
+
|
|
118
|
+
## Phase B — Distill the artifacts
|
|
119
|
+
|
|
120
|
+
Only after convergence. Produce (and, if any new project-wide invariant surfaced in
|
|
121
|
+
step 4, append it to **`CONSTITUTION.md`** — never let an inviolable rule slip into an
|
|
122
|
+
ADR where it could later be "traded away"):
|
|
123
|
+
|
|
124
|
+
1. **One ADR per decision worth recording** at `docs/adr/ADR-NNN-<slug>.md`, format:
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
# ADR-NNN: <title of the decision>
|
|
128
|
+
## Status: Accepted
|
|
129
|
+
<!-- Use Status: Experiment only for a bounded hypothesis with feedback/review criteria. -->
|
|
130
|
+
## Context
|
|
131
|
+
<the problem + the considered options: A) … B) … C) …>
|
|
132
|
+
## Decision
|
|
133
|
+
<the chosen option>
|
|
134
|
+
## Reasons
|
|
135
|
+
- <why, point by point>
|
|
136
|
+
## Consequences
|
|
137
|
+
+ <the good>
|
|
138
|
+
- <the cost / what it forces on us>
|
|
139
|
+
<!-- If Status: Experiment, also include:
|
|
140
|
+
## Hypothesis
|
|
141
|
+
## Feedback Signal
|
|
142
|
+
## Review By: YYYY-MM-DD (or ## Review Trigger)
|
|
143
|
+
## Promote Criteria
|
|
144
|
+
## Rollback / Supersede Criteria
|
|
145
|
+
-->
|
|
146
|
+
## Implementation Plan
|
|
147
|
+
- Affected paths: <files/dirs>
|
|
148
|
+
- Patterns: <pattern to follow>
|
|
149
|
+
- Tests: <which tests prove the decision>
|
|
150
|
+
## Verification
|
|
151
|
+
- [ ] <criterion checkable by an agent>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Number ADRs continuing from the highest existing one in `docs/adr/` (glob first).
|
|
155
|
+
Negative decisions count: "what we are NOT going to use and why" is a valid ADR.
|
|
156
|
+
Experimental decisions count only when they are explicit hypotheses with feedback signal,
|
|
157
|
+
review date/trigger, promote criteria and rollback/supersede criteria. Do not use
|
|
158
|
+
`Status: Experiment` as a polite way to avoid deciding.
|
|
159
|
+
|
|
160
|
+
2. **`ACCEPTANCE.md`** at the repo root (or the path in `uscha.config.json` →
|
|
161
|
+
`defaults.acceptance_file`). This is the file dev-loop's readiness measures — it MUST
|
|
162
|
+
exist and be checkable:
|
|
163
|
+
|
|
164
|
+
```markdown
|
|
165
|
+
# Acceptance — <feature>
|
|
166
|
+
## Definition of Done
|
|
167
|
+
- [ ] AC-01 — <verifiable criterion>
|
|
168
|
+
- [ ] AC-02 — <verifiable criterion>
|
|
169
|
+
## How we measure success
|
|
170
|
+
- <objective metric: p95, cost, zero orphans, …>
|
|
171
|
+
## Out of scope
|
|
172
|
+
- <boundary> → <future spec>
|
|
173
|
+
## Recorded decisions
|
|
174
|
+
- ADR-NNN — <title>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Where to write:** with file tools available (Claude Code), write the files to disk.
|
|
178
|
+
In a chat-only context, print each file in a fenced block, clearly labeled with its
|
|
179
|
+
target path, ready to paste — and remind the human these go to `docs/adr/` and the repo
|
|
180
|
+
root before running dev-loop.
|
|
181
|
+
|
|
182
|
+
**Tracked-markdown protocol:** if any target `.md` already exists and is tracked, ask
|
|
183
|
+
for its current version before overwriting — never silently replace.
|
|
184
|
+
|
|
185
|
+
## Handoff to dev-loop
|
|
186
|
+
|
|
187
|
+
Close with the handoff prompt so the build phase starts by planning, not improvising:
|
|
188
|
+
|
|
189
|
+
> "Read the ADR set and ACCEPTANCE.md. Before touching code: 1) summarize the plan of
|
|
190
|
+
> files to create/modify, 2) confirm which decisions were left implicit, 3)
|
|
191
|
+
> show me the first test you would write."
|
|
192
|
+
|
|
193
|
+
Two-command flow end to end: `/uscha-adr-refine` → (ADR set + ACCEPTANCE.md) → `/uscha-devloop`.
|
|
194
|
+
|
|
195
|
+
That route is the **nominal** one, not the answer: the `Next:`/`Run:` you emit in the close block are DERIVED from the state you actually produced, and override it whenever an open experiment, an unclosed spike, an unapproved golden or a red gate stands in between.
|
|
196
|
+
|
|
197
|
+
## Anti-patterns (do not do)
|
|
198
|
+
|
|
199
|
+
- Generate an ADR from a one-line request without interviewing.
|
|
200
|
+
- Accept "do it however you want" on a consequential decision without recording the
|
|
201
|
+
assumption.
|
|
202
|
+
- Write an ACCEPTANCE item that isn't objectively checkable ("that it works well").
|
|
203
|
+
- Emit artifacts before the convergence conditions are met.
|