@miller-tech/uap 1.40.0 → 1.41.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 +109 -642
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli/deliver-defaults.d.ts +23 -0
- package/dist/cli/deliver-defaults.d.ts.map +1 -0
- package/dist/cli/deliver-defaults.js +121 -0
- package/dist/cli/deliver-defaults.js.map +1 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +29 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +19 -0
- package/dist/cli/setup.js.map +1 -1
- package/dist/policies/policy-tools.d.ts +7 -0
- package/dist/policies/policy-tools.d.ts.map +1 -1
- package/dist/policies/policy-tools.js +24 -2
- package/dist/policies/policy-tools.js.map +1 -1
- package/docs/INDEX.md +48 -286
- package/docs/architecture/OVERVIEW.md +328 -0
- package/docs/architecture/PROTOCOL.md +204 -0
- package/docs/benchmarks/README.md +17 -192
- package/docs/getting-started/CONFIGURATION.md +237 -0
- package/docs/getting-started/INSTALLATION.md +125 -0
- package/docs/getting-started/QUICKSTART.md +115 -0
- package/docs/guides/COORDINATION.md +162 -0
- package/docs/guides/DELIVER.md +115 -0
- package/docs/guides/DEPLOY_BATCHING.md +212 -0
- package/docs/guides/DROIDS_AND_SKILLS.md +202 -0
- package/docs/guides/LOCAL_MODELS.md +148 -0
- package/docs/guides/MCP_ROUTER.md +195 -0
- package/docs/guides/MEMORY.md +235 -0
- package/docs/guides/MULTI_MODEL.md +223 -0
- package/docs/guides/POLICIES.md +190 -0
- package/docs/guides/WORKTREE_WORKFLOW.md +185 -0
- package/docs/integrations/MCP_ROUTER.md +147 -0
- package/docs/integrations/RTK.md +102 -0
- package/docs/reference/API.md +485 -0
- package/docs/reference/CLI.md +719 -0
- package/docs/reference/CONFIGURATION.md +90 -193
- package/docs/reference/DATABASE_SCHEMA.md +110 -344
- package/docs/reference/FEATURES.md +176 -472
- package/docs/reference/PATTERNS.md +102 -0
- package/docs/reference/PLATFORMS.md +83 -0
- package/package.json +3 -1
- package/src/policies/enforcers/7ebbc721-7540-4e9f-879a-770e0213a09b_architecture_review.py +101 -0
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/_common.py +100 -0
- package/src/policies/enforcers/artifact_hygiene.py +52 -0
- package/src/policies/enforcers/cluster_routing.py +63 -0
- package/src/policies/enforcers/codebase_read_before_plan.py +52 -0
- package/src/policies/enforcers/coord_overlap.py +81 -0
- package/src/policies/enforcers/delivery_enforcement.py +97 -0
- package/src/policies/enforcers/doc_live_over_report.py +50 -0
- package/src/policies/enforcers/expert_review_required.py +135 -0
- package/src/policies/enforcers/iac_parity.py +53 -0
- package/src/policies/enforcers/mcp_router_first.py +37 -0
- package/src/policies/enforcers/memory_before_plan.py +61 -0
- package/src/policies/enforcers/parallel_reads.py +50 -0
- package/src/policies/enforcers/rtk_wrap.py +44 -0
- package/src/policies/enforcers/schema_diff_gate.py +80 -0
- package/src/policies/enforcers/session_memory_write.py +52 -0
- package/src/policies/enforcers/task_required.py +131 -0
- package/src/policies/enforcers/test_gate.py +58 -0
- package/src/policies/enforcers/validate_plan_before_build.py +75 -0
- package/src/policies/enforcers/worktree_required.py +57 -0
- package/src/policies/schemas/policies/architecture-review.md +51 -0
- package/src/policies/schemas/policies/artifact-hygiene.md +29 -0
- package/src/policies/schemas/policies/cluster-routing.md +31 -0
- package/src/policies/schemas/policies/codebase-read-before-plan.md +30 -0
- package/src/policies/schemas/policies/coord-overlap.md +24 -0
- package/src/policies/schemas/policies/delivery-enforcement.md +45 -0
- package/src/policies/schemas/policies/doc-live-over-report.md +32 -0
- package/src/policies/schemas/policies/expert-review-required.md +60 -0
- package/src/policies/schemas/policies/iac-parity.md +31 -0
- package/src/policies/schemas/policies/mandatory-testing-deployment.md +147 -0
- package/src/policies/schemas/policies/mcp-router-first.md +24 -0
- package/src/policies/schemas/policies/memory-before-plan.md +24 -0
- package/src/policies/schemas/policies/merge-deploy-monitor-verify.md +145 -0
- package/src/policies/schemas/policies/parallel-reads.md +24 -0
- package/src/policies/schemas/policies/rtk-wrap.md +26 -0
- package/src/policies/schemas/policies/schema-diff-gate.md +30 -0
- package/src/policies/schemas/policies/session-memory-write.md +24 -0
- package/src/policies/schemas/policies/task-required.md +49 -0
- package/src/policies/schemas/policies/test-gate.md +24 -0
- package/src/policies/schemas/policies/validate-plan-before-build.md +28 -0
- package/src/policies/schemas/policies/worktree-required.md +28 -0
- package/templates/hooks/uap-policy-gate.sh +5 -0
- package/docs/AGENTS.md +0 -423
- package/docs/DOCUMENTATION_AUDIT_REPORT.md +0 -131
- package/docs/GETTING_STARTED.md +0 -288
- package/docs/PROJECT_ANALYSIS_REPORT.md +0 -510
- package/docs/architecture/COMPLETE_ARCHITECTURE.md +0 -748
- package/docs/architecture/EXPERT_STACK.md +0 -137
- package/docs/architecture/MULTI_MODEL.md +0 -224
- package/docs/architecture/PLATFORM_GATING.md +0 -68
- package/docs/architecture/SYSTEM_ANALYSIS.md +0 -334
- package/docs/architecture/UAP_COMPLIANCE.md +0 -217
- package/docs/architecture/UAP_PROTOCOL.md +0 -339
- package/docs/architecture/UAP_STRICT_DROIDS.md +0 -172
- package/docs/archive/BALLS_MODE_SELF_ANALYSIS.md +0 -260
- package/docs/archive/BENCHMARK_GAPS_AND_PLAN.md +0 -146
- package/docs/archive/FAILING_TASKS_SOLUTION_PLAN.md +0 -668
- package/docs/archive/JINJA2-SYSTEM-MESSAGE-FIX.md +0 -209
- package/docs/archive/MODEL_ROUTING_IMPLEMENTATION_SUMMARY.md +0 -281
- package/docs/archive/MODEL_ROUTING_OPTIMIZATION_PLAN.md +0 -320
- package/docs/archive/NPM-PUBLISH-V0.9.1.md +0 -240
- package/docs/archive/OPTIMIZATION_OPTIONS.md +0 -334
- package/docs/archive/PARALLELISM_GAPS_AND_OPTIONS.md +0 -422
- package/docs/archive/POLICY_GATE_IMPLEMENTATION.md +0 -245
- package/docs/archive/SETUP_IMPROVEMENTS.md +0 -213
- package/docs/archive/UAP_GENERIC_OPTIMIZATION_PLAN.md +0 -270
- package/docs/archive/UAP_OPTIMIZATION_PLAN.md +0 -701
- package/docs/archive/UAP_V103_PATTERN_DESIGN.md +0 -315
- package/docs/archive/UAP_V104_COMPLIANCE_DESIGN.md +0 -223
- package/docs/archive/changelog/2026-03-10_uap-100-compliance.md +0 -77
- package/docs/archive/changelog/2026-03-10_uap-full-system-verification.md +0 -109
- package/docs/archive/opencode-integration-guide.md +0 -740
- package/docs/archive/opencode-integration-quickref.md +0 -180
- package/docs/benchmarks/OVERNIGHT_RUNNER.md +0 -341
- package/docs/benchmarks/SPECULATIVE_DECODING_JOURNEY_2026-03.md +0 -221
- package/docs/benchmarks/VALIDATION_PLAN.md +0 -568
- package/docs/blog/SPECULATIVE_DECODING_PRODUCTION_PLAYBOOK.md +0 -139
- package/docs/blog/local-coding-agents.md +0 -266
- package/docs/blog/x-thread.md +0 -254
- package/docs/deployment/DEPLOYMENT.md +0 -895
- package/docs/deployment/DEPLOYMENT_STRATEGIES.md +0 -518
- package/docs/deployment/DEPLOY_BATCHER_ANALYSIS.md +0 -224
- package/docs/deployment/DEPLOY_BATCHING.md +0 -273
- package/docs/deployment/DEPLOY_BUCKETING_ANALYSIS.md +0 -420
- package/docs/deployment/QWEN35_LLAMA_CPP.md +0 -426
- package/docs/deployment/UAP_LLAMA_ANTHROPIC_PROXY_BOOTSTRAP.md +0 -279
- package/docs/getting-started/INTEGRATION.md +0 -628
- package/docs/getting-started/OVERVIEW.md +0 -324
- package/docs/getting-started/SETUP.md +0 -377
- package/docs/integrations/MCP_ROUTER_SETUP.md +0 -445
- package/docs/integrations/RTK_INTEGRATION.md +0 -468
- package/docs/operations/TROUBLESHOOTING.md +0 -660
- package/docs/pr/PR_SPECULATIVE_DOCS_TEMPLATE.md +0 -146
- package/docs/pr/UPSTREAM_PRS.md +0 -424
- package/docs/reference/API_REFERENCE.md +0 -903
- package/docs/reference/EXPERT_DROIDS.md +0 -219
- package/docs/reference/HARNESS-MATRIX.md +0 -318
- package/docs/reference/PATTERN_LIBRARY.md +0 -636
- package/docs/reference/UAP_CLI_REFERENCE.md +0 -620
- package/docs/research/BEHAVIORAL_PATTERNS.md +0 -228
- package/docs/research/DOMAIN_STRATEGIES.md +0 -316
- package/docs/research/MEMORY_SYSTEMS_COMPARISON.md +0 -812
- package/docs/research/PATTERN_ANALYSIS_2026-01-18.md +0 -436
- package/docs/research/PERFORMANCE_ANALYSIS_2026-01-18.md +0 -209
- package/docs/research/PERFORMANCE_TEST_PLAN.md +0 -383
- package/docs/research/TERMINAL_BENCH_LEARNINGS.md +0 -217
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""task-required enforcer: a UAP task must be in_progress before mutating work.
|
|
3
|
+
|
|
4
|
+
Blocks Edit/Write/MultiEdit (outside exempt prefixes) and the ship actions
|
|
5
|
+
git commit / git push / gh pr create when no row in .uap/tasks/tasks.db has
|
|
6
|
+
status='in_progress'. Closes UAP protocol step 4 — which was previously
|
|
7
|
+
text-injection only and therefore skippable.
|
|
8
|
+
|
|
9
|
+
Fail-open: if UAP task tracking is not initialised (no tasks.db) or the DB is
|
|
10
|
+
unreadable, the operation is allowed — so non-UAP repos are unaffected.
|
|
11
|
+
Override: set UAP_NO_TASK=1 to bypass.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sqlite3
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
21
|
+
from _common import emit, parse_cli, repo_root, run # noqa: E402
|
|
22
|
+
|
|
23
|
+
EDIT_OPS = {"edit", "write", "multiedit"}
|
|
24
|
+
|
|
25
|
+
# Meta / infra / docs paths that do not require a task (mirror worktree_required,
|
|
26
|
+
# plus .policy-tools/ which is the policy-system's own runtime artifact dir).
|
|
27
|
+
EXEMPT_PREFIXES = (
|
|
28
|
+
".claude/",
|
|
29
|
+
".cursor/",
|
|
30
|
+
".opencode/",
|
|
31
|
+
".codex/",
|
|
32
|
+
".forge/",
|
|
33
|
+
".uap/",
|
|
34
|
+
".policy-tools/",
|
|
35
|
+
"src/policies/",
|
|
36
|
+
"scripts/",
|
|
37
|
+
"docs/",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Bash ship actions gated even though Bash itself is otherwise unrestricted.
|
|
41
|
+
SHIP_PATTERNS = (
|
|
42
|
+
re.compile(r"\bgit\s+(commit|push)\b"),
|
|
43
|
+
re.compile(r"\bgh\s+pr\s+create\b"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main_repo_root() -> Path:
|
|
48
|
+
"""Resolve the primary worktree root, even when invoked from a linked
|
|
49
|
+
worktree — `git rev-parse --git-common-dir` always points at the main
|
|
50
|
+
.git, whose parent is the primary checkout."""
|
|
51
|
+
rc, out, _ = run(["git", "rev-parse", "--git-common-dir"])
|
|
52
|
+
if rc == 0 and out.strip():
|
|
53
|
+
p = Path(out.strip())
|
|
54
|
+
if not p.is_absolute():
|
|
55
|
+
p = (Path.cwd() / p).resolve()
|
|
56
|
+
return p.parent
|
|
57
|
+
return repo_root()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def in_progress_task_state():
|
|
61
|
+
"""True if an in_progress task exists, False if none, None if UAP task
|
|
62
|
+
tracking is not set up / DB unreadable (caller treats None as allow)."""
|
|
63
|
+
db = main_repo_root() / ".uap" / "tasks" / "tasks.db"
|
|
64
|
+
if not db.exists():
|
|
65
|
+
return None
|
|
66
|
+
try:
|
|
67
|
+
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True, timeout=2)
|
|
68
|
+
try:
|
|
69
|
+
n = con.execute(
|
|
70
|
+
"SELECT COUNT(*) FROM tasks WHERE status='in_progress'"
|
|
71
|
+
).fetchone()[0]
|
|
72
|
+
finally:
|
|
73
|
+
con.close()
|
|
74
|
+
return n > 0
|
|
75
|
+
except Exception: # noqa: BLE001
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main() -> None:
|
|
80
|
+
op, args = parse_cli()
|
|
81
|
+
|
|
82
|
+
if os.environ.get("UAP_NO_TASK") == "1":
|
|
83
|
+
emit(True, "UAP_NO_TASK override set")
|
|
84
|
+
|
|
85
|
+
op_l = op.lower()
|
|
86
|
+
is_edit = op_l in EDIT_OPS
|
|
87
|
+
is_bash = op_l == "bash"
|
|
88
|
+
if not is_edit and not is_bash:
|
|
89
|
+
emit(True, "not a mutating operation")
|
|
90
|
+
|
|
91
|
+
if is_bash:
|
|
92
|
+
cmd = args.get("command") or args.get("cmd") or ""
|
|
93
|
+
if not any(p.search(cmd) for p in SHIP_PATTERNS):
|
|
94
|
+
emit(True, "not a ship action")
|
|
95
|
+
gate_label = "ship action (git commit/push, gh pr create)"
|
|
96
|
+
else:
|
|
97
|
+
target = (
|
|
98
|
+
args.get("file_path")
|
|
99
|
+
or args.get("path")
|
|
100
|
+
or args.get("target")
|
|
101
|
+
or ""
|
|
102
|
+
)
|
|
103
|
+
if not target:
|
|
104
|
+
emit(True, "no file path in args")
|
|
105
|
+
root = repo_root()
|
|
106
|
+
try:
|
|
107
|
+
rel = str(Path(target).resolve().relative_to(root))
|
|
108
|
+
except ValueError:
|
|
109
|
+
emit(True, "target outside repo")
|
|
110
|
+
if any(rel.startswith(p) for p in EXEMPT_PREFIXES):
|
|
111
|
+
emit(True, f"exempt path: {rel}")
|
|
112
|
+
gate_label = f"edit of '{rel}'"
|
|
113
|
+
|
|
114
|
+
state = in_progress_task_state()
|
|
115
|
+
if state is None:
|
|
116
|
+
emit(True, "UAP task tracking not initialised — fail-open")
|
|
117
|
+
if state:
|
|
118
|
+
emit(True, "in_progress UAP task present")
|
|
119
|
+
|
|
120
|
+
emit(
|
|
121
|
+
False,
|
|
122
|
+
f"task-required: no in_progress UAP task — {gate_label} blocked. "
|
|
123
|
+
'Run: uap task create --type <task|bug|feature> --title "<desc>" '
|
|
124
|
+
"then: uap task update <id> --status in_progress "
|
|
125
|
+
"(or: uap task claim <id> to also spin a worktree). "
|
|
126
|
+
"Override for one-off meta-work: UAP_NO_TASK=1.",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
if __name__ == "__main__":
|
|
131
|
+
main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""test-gate enforcer: changed services under services|apps need test deltas."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
9
|
+
from _common import emit, parse_cli, run, worktree_root # noqa: E402
|
|
10
|
+
|
|
11
|
+
PR_OPS_RE = re.compile(
|
|
12
|
+
r"\b(pr[-_ ]?ready|pr-create|gh pr create|signoff|ready[-_ ]for[-_ ]review|merge)\b",
|
|
13
|
+
re.I,
|
|
14
|
+
)
|
|
15
|
+
SVC_RE = re.compile(r"^(services|apps)/([^/]+)/")
|
|
16
|
+
TEST_RE = re.compile(
|
|
17
|
+
r"(/tests?/|__tests__/|\.test\.(ts|tsx|js|py)$|_test\.(py|go)$|\.spec\.(ts|tsx|js)$)"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> None:
|
|
22
|
+
op, args = parse_cli()
|
|
23
|
+
cmd = (args.get("command") or "").lower()
|
|
24
|
+
if not (PR_OPS_RE.search(op) or PR_OPS_RE.search(cmd)):
|
|
25
|
+
emit(True, "not a PR-ready gate point")
|
|
26
|
+
|
|
27
|
+
root = worktree_root() # git diff must run against the working tree, not MAIN_ROOT
|
|
28
|
+
rc, out, _ = run(
|
|
29
|
+
["git", "diff", "--name-only", "origin/main...HEAD"], cwd=root, timeout=10
|
|
30
|
+
)
|
|
31
|
+
if rc != 0:
|
|
32
|
+
emit(True, "cannot compute diff vs origin/main")
|
|
33
|
+
|
|
34
|
+
changed = [l for l in out.splitlines() if l.strip()]
|
|
35
|
+
svcs_touched: set[str] = set()
|
|
36
|
+
for f in changed:
|
|
37
|
+
m = SVC_RE.match(f)
|
|
38
|
+
if m:
|
|
39
|
+
svcs_touched.add(f"{m.group(1)}/{m.group(2)}")
|
|
40
|
+
|
|
41
|
+
if not svcs_touched:
|
|
42
|
+
emit(True, "no services/apps changes in diff")
|
|
43
|
+
|
|
44
|
+
svcs_with_tests = {
|
|
45
|
+
s for s in svcs_touched if any(f.startswith(s) and TEST_RE.search(f) for f in changed)
|
|
46
|
+
}
|
|
47
|
+
missing = svcs_touched - svcs_with_tests
|
|
48
|
+
if missing:
|
|
49
|
+
emit(
|
|
50
|
+
False,
|
|
51
|
+
f"test-gate: the following services changed without test deltas: {', '.join(sorted(missing))}",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
emit(True, "all changed services include test deltas")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""validate-plan-before-build enforcer.
|
|
3
|
+
|
|
4
|
+
On the first mutating tool call after a plan is marked ready, block and require
|
|
5
|
+
the agent to run the `validate the plan` prompt. State tracked in .uap/plan_state.json.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
15
|
+
from _common import emit, parse_cli # noqa: E402
|
|
16
|
+
|
|
17
|
+
STATE = Path(os.environ.get("UAP_STATE_DIR", ".uap")) / "plan_state.json"
|
|
18
|
+
MUTATING_OPS = {"Edit", "Write", "MultiEdit", "edit", "write", "multiedit"}
|
|
19
|
+
BUILD_BASH_RE = (
|
|
20
|
+
"git commit",
|
|
21
|
+
"git push",
|
|
22
|
+
"kubectl apply",
|
|
23
|
+
"helm upgrade",
|
|
24
|
+
"helm install",
|
|
25
|
+
"terraform apply",
|
|
26
|
+
"npm run build",
|
|
27
|
+
"pnpm build",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_state() -> dict:
|
|
32
|
+
if not STATE.exists():
|
|
33
|
+
return {}
|
|
34
|
+
try:
|
|
35
|
+
return json.loads(STATE.read_text())
|
|
36
|
+
except json.JSONDecodeError:
|
|
37
|
+
return {}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def save_state(s: dict) -> None:
|
|
41
|
+
STATE.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
STATE.write_text(json.dumps(s))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def main() -> None:
|
|
46
|
+
op, args = parse_cli()
|
|
47
|
+
state = load_state()
|
|
48
|
+
|
|
49
|
+
# Mutating?
|
|
50
|
+
cmd = (args.get("command") or "").lower()
|
|
51
|
+
mutating = op in MUTATING_OPS or any(p in cmd for p in BUILD_BASH_RE)
|
|
52
|
+
if not mutating:
|
|
53
|
+
emit(True, "not a build/mutation op")
|
|
54
|
+
|
|
55
|
+
plan_ready = bool(state.get("plan_ready"))
|
|
56
|
+
validated_at = float(state.get("validated_at", 0))
|
|
57
|
+
ready_at = float(state.get("ready_at", 0))
|
|
58
|
+
|
|
59
|
+
if not plan_ready:
|
|
60
|
+
emit(True, "no active plan-ready marker")
|
|
61
|
+
|
|
62
|
+
if validated_at and validated_at >= ready_at:
|
|
63
|
+
emit(True, "plan validated since marking ready")
|
|
64
|
+
|
|
65
|
+
emit(
|
|
66
|
+
False,
|
|
67
|
+
"validate-plan-before-build: plan is ready but not validated. "
|
|
68
|
+
"Run the prompt `validate the plan` before making changes. "
|
|
69
|
+
"After a pass, write {\"validated_at\": <epoch>} to .uap/plan_state.json.",
|
|
70
|
+
inject_prompt="validate the plan",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""worktree-required enforcer: Edit/Write must target a .worktrees/ path."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
9
|
+
from _common import emit, parse_cli, repo_root # noqa: E402
|
|
10
|
+
|
|
11
|
+
EXEMPT_PREFIXES = (
|
|
12
|
+
".claude/",
|
|
13
|
+
".cursor/",
|
|
14
|
+
".opencode/",
|
|
15
|
+
".codex/",
|
|
16
|
+
".forge/",
|
|
17
|
+
".uap/",
|
|
18
|
+
"src/policies/",
|
|
19
|
+
"scripts/",
|
|
20
|
+
"docs/",
|
|
21
|
+
)
|
|
22
|
+
EDIT_OPS = {"Edit", "Write", "MultiEdit", "edit", "write", "multiedit"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main() -> None:
|
|
26
|
+
op, args = parse_cli()
|
|
27
|
+
if op not in EDIT_OPS:
|
|
28
|
+
emit(True, "not a file-edit operation")
|
|
29
|
+
|
|
30
|
+
target = args.get("file_path") or args.get("path") or args.get("target") or ""
|
|
31
|
+
if not target:
|
|
32
|
+
emit(True, "no file path in args")
|
|
33
|
+
|
|
34
|
+
root = repo_root()
|
|
35
|
+
try:
|
|
36
|
+
rel = str(Path(target).resolve().relative_to(root))
|
|
37
|
+
except ValueError:
|
|
38
|
+
emit(True, "target outside repo")
|
|
39
|
+
|
|
40
|
+
if rel.startswith(".worktrees/"):
|
|
41
|
+
emit(True, "target inside a worktree")
|
|
42
|
+
|
|
43
|
+
if any(rel.startswith(p) for p in EXEMPT_PREFIXES):
|
|
44
|
+
emit(True, f"exempt path: {rel}")
|
|
45
|
+
|
|
46
|
+
if os.environ.get("UAP_NO_WORKTREE") == "1":
|
|
47
|
+
emit(True, "UAP_NO_WORKTREE override set")
|
|
48
|
+
|
|
49
|
+
emit(
|
|
50
|
+
False,
|
|
51
|
+
f"worktree-required: '{rel}' must be edited inside .worktrees/NNN-<slug>/. "
|
|
52
|
+
"Run: uap worktree create <slug>",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# architecture-review
|
|
2
|
+
|
|
3
|
+
**Category**: quality
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: review
|
|
6
|
+
**Tags**: uap, architecture, review, adr, enforcement
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
When a PR-ready / merge operation is attempted and the diff vs. upstream touches
|
|
11
|
+
architecturally significant paths, the change MUST be accompanied by either an
|
|
12
|
+
ADR or an active waiver. Qualifying ("trigger") paths:
|
|
13
|
+
|
|
14
|
+
- `src/types/**`
|
|
15
|
+
- `**/schemas/**`
|
|
16
|
+
- `src/index.ts`
|
|
17
|
+
- `docs/architecture/**` (excluding `docs/architecture/adr/`)
|
|
18
|
+
- `src/coordination/capability-router.ts`, `src/coordination/pattern-router.ts`
|
|
19
|
+
|
|
20
|
+
The requirement is satisfied by either:
|
|
21
|
+
|
|
22
|
+
- an ADR under `docs/architecture/adr/*.md` added or modified in the same diff, or
|
|
23
|
+
- an active waiver `policies/waivers/*architecture-review*.md`.
|
|
24
|
+
|
|
25
|
+
Otherwise the ship/merge op is blocked.
|
|
26
|
+
|
|
27
|
+
## Why
|
|
28
|
+
|
|
29
|
+
This policy backs the `architect-reviewer` droid's stated authority. The
|
|
30
|
+
enforcer already existed and ran, but had **no policy document** describing it —
|
|
31
|
+
agents and reviewers had no canonical reference for when architecture review is
|
|
32
|
+
required or how to satisfy it. Significant decisions (public types, schemas,
|
|
33
|
+
top-level exports, routing logic) carry high blast radius and cost of reversal;
|
|
34
|
+
requiring an ADR (or an explicit waiver) ensures they are recorded and reviewed
|
|
35
|
+
rather than slipping through in an unrelated change.
|
|
36
|
+
|
|
37
|
+
## Enforcement
|
|
38
|
+
|
|
39
|
+
Python enforcer `architecture_review.py` fires on PR-ready/merge operations,
|
|
40
|
+
computes `git diff --name-only origin/master...HEAD` (falling back to
|
|
41
|
+
`origin/main`), matches the trigger paths, and blocks unless an ADR is present
|
|
42
|
+
in the diff or a matching waiver exists.
|
|
43
|
+
|
|
44
|
+
Fail-open: if the upstream diff cannot be computed, the operation is allowed.
|
|
45
|
+
Waivers are granted by `compliance-officer`.
|
|
46
|
+
|
|
47
|
+
```rules
|
|
48
|
+
- title: "Architecturally significant diffs require an ADR or waiver before merge"
|
|
49
|
+
keywords: [merge, pr-ready, gh pr create, signoff, ready-for-review, src/types, schemas, src/index.ts]
|
|
50
|
+
antiPatterns: [no-adr, unreviewed-architecture, skip-architecture-review]
|
|
51
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# artifact-hygiene
|
|
2
|
+
|
|
3
|
+
**Category**: quality
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: git, hygiene, artifacts
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
Binary artifacts (`*.png`, `*.jpg`, `*.pdf`, `*.zip`, `*.tar.gz`, `*.db`, `*.sqlite*`) MUST NOT be committed outside:
|
|
11
|
+
|
|
12
|
+
- `docs/**`
|
|
13
|
+
- `tests/**/__screenshots__/**`
|
|
14
|
+
- `apps/**/public/**`, `apps/**/static/**`, `apps/**/assets/**`
|
|
15
|
+
- `agents/data/memory/**` (scoped state)
|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
Repo root currently has 80+ loose audit PNGs, screenshots, and stale DBs — bloats the repo, confuses reviewers, breaks shallow clones. Keeping artifacts in curated subdirs preserves git performance.
|
|
20
|
+
|
|
21
|
+
## Enforcement
|
|
22
|
+
|
|
23
|
+
Python enforcer `artifact_hygiene.py` inspects `git status` / new Write targets and rejects blocked paths.
|
|
24
|
+
|
|
25
|
+
```rules
|
|
26
|
+
- title: "Binary artifacts belong in curated dirs"
|
|
27
|
+
keywords: [write, create-file, commit, git-add]
|
|
28
|
+
antiPatterns: [.png, .jpg, .zip, .tar.gz, .sqlite]
|
|
29
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# cluster-routing
|
|
2
|
+
|
|
3
|
+
**Category**: infrastructure
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: kubernetes, istio, multi-cluster, iac
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
`kubectl apply|patch|create|edit|delete` and `helm install|upgrade|uninstall` MUST target the cluster context matching the component's domain:
|
|
11
|
+
|
|
12
|
+
- **Observability** (Grafana, Prometheus, OpenObserve, Fluent Bit, ServiceMonitor, alerts, dashboards) → `do-syd1-pay2u-openobserve`
|
|
13
|
+
- **Authentication / Identity** (Zitadel, OIDC, IAM CRDs) → `do-syd1-zitadel`
|
|
14
|
+
- **Everything else** (apps, APIs, CMS, web, ML services, PgDog, Redis, Postgres/CNPG) → `do-syd1-pay2u`
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
AGENTS.md codifies the 3-cluster split. Cross-cluster mistakes cost 10–30 min per rollback plus reconciliation. Cross-cluster traffic MUST use public HTTPS URLs, never cluster-internal DNS.
|
|
19
|
+
|
|
20
|
+
## Enforcement
|
|
21
|
+
|
|
22
|
+
Python enforcer `cluster_routing.py` checks `kubectl config current-context` against the manifest's domain before allowing the command.
|
|
23
|
+
|
|
24
|
+
```rules
|
|
25
|
+
- title: "kubectl context must match component domain"
|
|
26
|
+
keywords: [kubectl, helm, apply, patch, install, upgrade]
|
|
27
|
+
antiPatterns: [wrong-context, cross-cluster-dns]
|
|
28
|
+
- title: "Cross-cluster calls must use public HTTPS"
|
|
29
|
+
keywords: [cross-cluster, service-mesh]
|
|
30
|
+
antiPatterns: [svc.cluster.local, internal-dns]
|
|
31
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# codebase-read-before-plan
|
|
2
|
+
|
|
3
|
+
**Category**: workflow
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: planning, exploration, accuracy
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
Before emitting any implementation plan (plans with ≥2 steps or touching any code path), the agent MUST have read the relevant existing codebase in the same session:
|
|
11
|
+
|
|
12
|
+
- At least one `Read` of a file in the target service/app, OR
|
|
13
|
+
- At least one `Grep` / `Glob` over the target paths, OR
|
|
14
|
+
- A completed `Agent(subagent_type=Explore)` for the target domain
|
|
15
|
+
|
|
16
|
+
Plans produced without this evidence are rejected.
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
Planning without reading generates drift-prone, hallucinated plans that conflict with existing conventions. User's directive: ground plans in the actual codebase first.
|
|
21
|
+
|
|
22
|
+
## Enforcement
|
|
23
|
+
|
|
24
|
+
Python enforcer `codebase_read_before_plan.py` scans the recent tool-call log for read operations against files within the plan's declared scope; blocks plan emission if none found.
|
|
25
|
+
|
|
26
|
+
```rules
|
|
27
|
+
- title: "Plans must follow codebase reads"
|
|
28
|
+
keywords: [plan, design, propose, architect, implement]
|
|
29
|
+
antiPatterns: [plan-without-read, unread-scope, blind-plan]
|
|
30
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coord-overlap
|
|
2
|
+
|
|
3
|
+
**Category**: workflow
|
|
4
|
+
**Level**: RECOMMENDED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: agents, coordination, parallelism
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
Before spawning a second or subsequent Agent/sub-agent that will write files, call `uap coordination check <paths>` to detect overlap with in-flight agents.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
Multi-harness setup (.claude, .cursor, .opencode, .codex, .forge all present) creates collision risk. Overlap causes lost work and merge pain. `uap coordination check` is already available — unused.
|
|
15
|
+
|
|
16
|
+
## Enforcement
|
|
17
|
+
|
|
18
|
+
Python enforcer `coord_overlap.py` queries `agents/data/coordination/coordination.db` for active reservations on the target paths and blocks if conflicts exist.
|
|
19
|
+
|
|
20
|
+
```rules
|
|
21
|
+
- title: "Parallel agents require overlap check"
|
|
22
|
+
keywords: [agent, spawn, subagent, parallel, delegate]
|
|
23
|
+
antiPatterns: [skip-coord, no-reservation, overlap-ignore]
|
|
24
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# delivery-enforcement
|
|
2
|
+
|
|
3
|
+
**Category**: safety
|
|
4
|
+
**Level**: RECOMMENDED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: uap, delivery, deliver, convergence, enforcement
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
Substantive coding work SHOULD go through the `uap deliver` convergence loop,
|
|
11
|
+
which drives a model to verified completion against the project's real gates
|
|
12
|
+
(build, type-check, tests) rather than ad-hoc hand edits.
|
|
13
|
+
|
|
14
|
+
The enforcer fires on `Edit` / `Write` / `MultiEdit` operations targeting
|
|
15
|
+
source-code files. It is satisfied when any of the following holds:
|
|
16
|
+
|
|
17
|
+
- the edit runs inside a deliver-driven context (`UAP_DELIVER_ACTIVE=1`),
|
|
18
|
+
- an explicit operator override is set (`UAP_DELIVER_BYPASS=1`),
|
|
19
|
+
- the target is not source code, or is a docs/config/script/policy/test path.
|
|
20
|
+
|
|
21
|
+
Otherwise the policy applies.
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
`uap deliver` exists and is auto-routable (CLI + MCP `deliver` tool), and it
|
|
26
|
+
classifies task complexity to enable the right convergence aids automatically.
|
|
27
|
+
But nothing previously *encouraged or required* coding agents to use it — the
|
|
28
|
+
capability was available, not enforced. This policy closes that gap: it makes
|
|
29
|
+
the expectation explicit and, when a team opts in, enforces it.
|
|
30
|
+
|
|
31
|
+
## Enforcement
|
|
32
|
+
|
|
33
|
+
Python enforcer `delivery_enforcement.py`.
|
|
34
|
+
|
|
35
|
+
**Default mode is ADVISORY** — it always allows the edit and logs a one-line
|
|
36
|
+
nudge toward `uap deliver`. Installing the policy therefore never breaks normal
|
|
37
|
+
editing.
|
|
38
|
+
|
|
39
|
+
**Strict mode is opt-in** via `UAP_ENFORCE_DELIVERY=block`: a direct source
|
|
40
|
+
edit outside a deliver context is then blocked (exit 2) until the work is routed
|
|
41
|
+
through `uap deliver` or `UAP_DELIVER_BYPASS=1` is set.
|
|
42
|
+
|
|
43
|
+
Exempt by construction: non-source files; `docs/`, `scripts/`, `policies/`,
|
|
44
|
+
`src/policies/`, test files (deliver protects those itself); and tooling
|
|
45
|
+
dot-dirs (`.claude/`, `.uap/`, `.worktrees/`, …).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# doc-live-over-report
|
|
2
|
+
|
|
3
|
+
**Category**: quality
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: docs, hygiene, debt
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
New files matching these patterns under `infra/**`, `docs/**`, or repo root are BLOCKED:
|
|
11
|
+
|
|
12
|
+
- `*_REPORT.md`
|
|
13
|
+
- `*_COMPLETE.md`
|
|
14
|
+
- `*_SUMMARY.md`
|
|
15
|
+
- `*_FIX_<date>.md`, `*_<YYYY-MM-DD>.md`
|
|
16
|
+
- `*_PLAN.md` (use tasks, not doc files)
|
|
17
|
+
|
|
18
|
+
Agents MUST update canonical README.md / runbooks instead.
|
|
19
|
+
|
|
20
|
+
## Why
|
|
21
|
+
|
|
22
|
+
The repo contains ~30 retrospective markdown reports under `infra/` — they rot, nobody reads them, and they bury live docs. The user's global rule: truth lives in code + canonical docs, not dated reports.
|
|
23
|
+
|
|
24
|
+
## Enforcement
|
|
25
|
+
|
|
26
|
+
Python enforcer `doc_live_over_report.py` inspects the target path of `Write` operations and rejects matching filenames.
|
|
27
|
+
|
|
28
|
+
```rules
|
|
29
|
+
- title: "No dated retrospective doc files"
|
|
30
|
+
keywords: [write, create-file, markdown]
|
|
31
|
+
antiPatterns: [_REPORT.md, _COMPLETE.md, _SUMMARY.md, _PLAN.md, _FIX_]
|
|
32
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# expert-review-required
|
|
2
|
+
|
|
3
|
+
**Category**: quality
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: review
|
|
6
|
+
**Tags**: uap, review, quality, enforcement, parallel-expert-review
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
A parallel expert review MUST precede shipping a non-trivial change. When no
|
|
11
|
+
review artifact exists for the current branch (or the artifact is stale relative
|
|
12
|
+
to `HEAD`), the enforcer blocks the ship actions:
|
|
13
|
+
|
|
14
|
+
- `git commit`, `git push`
|
|
15
|
+
- `gh pr create`
|
|
16
|
+
- merge / pr-ready / signoff / ready-for-review operations
|
|
17
|
+
|
|
18
|
+
Review artifact: `.uap/reviews/<branch-slug>.json`, written by the
|
|
19
|
+
`parallel-expert-review` skill on consolidation. The slug is an **injective
|
|
20
|
+
percent-encoding** of the branch name (`%`→`%25`, `/`→`%2F`) so distinct refs
|
|
21
|
+
like `feature/foo` and `feature-foo` never collide on one artifact. Recognised
|
|
22
|
+
shape:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{ "branch": "<name>", "head": "<sha>", "verdict": "approve", "reviewers": ["code-quality-reviewer", "security-code-reviewer", "..."] }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If the artifact records a `branch` that differs from the current branch, or a
|
|
29
|
+
`head` that differs from the current `HEAD`, the review is rejected (mismatch /
|
|
30
|
+
stale) and the op is blocked until a fresh review is recorded. Including
|
|
31
|
+
`branch` and `head` is strongly recommended so the artifact unambiguously
|
|
32
|
+
identifies what it covers.
|
|
33
|
+
|
|
34
|
+
## Why
|
|
35
|
+
|
|
36
|
+
The `parallel-expert-review` skill (and the `architect-reviewer` droid) claim
|
|
37
|
+
review is "REQUIRED by policy", but no enforcer ever checked that a review
|
|
38
|
+
actually ran — the requirement was advisory and silently skippable. This policy
|
|
39
|
+
makes review a hard, artifact-backed gate: ship actions fail until the review
|
|
40
|
+
fan-out (code-quality, security, performance, documentation, test-coverage) has
|
|
41
|
+
run and its consolidated verdict is recorded for the current HEAD.
|
|
42
|
+
|
|
43
|
+
This is the review analogue of `task-required` and `worktree-required`: convert
|
|
44
|
+
a protocol step that was best-effort into an enforced precondition.
|
|
45
|
+
|
|
46
|
+
## Enforcement
|
|
47
|
+
|
|
48
|
+
Python enforcer `expert_review_required.py` resolves the current branch and
|
|
49
|
+
HEAD via git, then checks `.uap/reviews/<branch-slug>.json` (slug = branch name
|
|
50
|
+
with `/` → `-`). Missing artifact → block; present but `head` mismatch → block
|
|
51
|
+
(stale); present and current → allow.
|
|
52
|
+
|
|
53
|
+
Fail-open: if the branch/HEAD cannot be resolved (detached HEAD, non-git tree),
|
|
54
|
+
the operation is allowed. Override for one-off meta-work: `UAP_NO_REVIEW=1`.
|
|
55
|
+
|
|
56
|
+
```rules
|
|
57
|
+
- title: "A parallel expert review must precede ship"
|
|
58
|
+
keywords: [git commit, git push, gh pr create, merge, pr-ready, signoff, ready-for-review]
|
|
59
|
+
antiPatterns: [no-review, unreviewed-ship, skip-review]
|
|
60
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# iac-parity
|
|
2
|
+
|
|
3
|
+
**Category**: infrastructure
|
|
4
|
+
**Level**: REQUIRED
|
|
5
|
+
**Enforcement Stage**: pre-exec
|
|
6
|
+
**Tags**: terraform, helm, iac, drift, reproducibility
|
|
7
|
+
|
|
8
|
+
## Rule
|
|
9
|
+
|
|
10
|
+
Any live-state change MUST be paired with an IaC change in the same worktree:
|
|
11
|
+
|
|
12
|
+
- `kubectl patch|apply|edit|create|delete` → must also modify `infra/terraform/**`, `infra/helm_charts/**`, or `infra/kubernetes/**`
|
|
13
|
+
- Helm `--set` overrides → must also update `values.yaml`
|
|
14
|
+
- DigitalOcean / cloud console changes are forbidden; use Terraform
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
User's global rule: "always apply state changes to IaC to ensure reproducibility." The repo has ~30 `IAC_PARITY_*`/`DRIFT_ANALYSIS_*` retrospectives — each a drift incident. Catching at author-time eliminates the loop.
|
|
19
|
+
|
|
20
|
+
## Enforcement
|
|
21
|
+
|
|
22
|
+
Python enforcer `iac_parity.py` verifies the worktree has staged/unstaged diffs under the IaC paths when a mutating cluster command is issued.
|
|
23
|
+
|
|
24
|
+
```rules
|
|
25
|
+
- title: "Live state changes require IaC diff"
|
|
26
|
+
keywords: [kubectl, helm, doctl, terraform, aws, gcloud, apply, patch, create, delete, edit]
|
|
27
|
+
antiPatterns: [--force, --no-iac, manual-console]
|
|
28
|
+
- title: "No ad-hoc cloud console changes"
|
|
29
|
+
keywords: [doctl, aws, gcloud, console]
|
|
30
|
+
antiPatterns: [click-ops, manual-edit]
|
|
31
|
+
```
|