diff --git a/Ctl/Pipfile b/Ctl/Pipfile
index 0c7a304..1bd6308 100644
--- a/Ctl/Pipfile
+++ b/Ctl/Pipfile
@@ -14,7 +14,7 @@ tmpl = "==0.3.0"
 
 [packages]
 munge = "<1,>=0.4"
-cfu = ">=1.2.0,<2"
+cfu = ">=1.3.0,<2"
 grainy = ">=1.4.0,<2"
 git-url-parse = ">=1.1.0,<2"
 pluginmgr = ">=0.6"
diff --git a/Ctl/requirements.txt b/Ctl/requirements.txt
index b3582c5..0037aaa 100644
--- a/Ctl/requirements.txt
+++ b/Ctl/requirements.txt
@@ -1,5 +1,5 @@
 munge >=0.4, <1
-cfu >= 1.2.0, < 2
+cfu >= 1.3.0, < 2
 grainy >= 1.4.0, <2
 git-url-parse >= 1.1.0, <2
 pluginmgr >= 0.6
diff --git a/src/ctl/__init__.py b/src/ctl/__init__.py
index eb4a635..b9616df 100644
--- a/src/ctl/__init__.py
+++ b/src/ctl/__init__.py
@@ -4,6 +4,7 @@ import os
 from pkg_resources import get_distribution
 
 import confu.config
+import confu.exceptions
 import grainy.core
 import copy
 import logging
@@ -279,11 +280,14 @@ class Ctl(object):
     # def set_config_dir(self):
 
     def __init__(self, ctx=None, config_dir=None, full_init=True):
-        self.init_context(ctx=ctx, config_dir=config_dir)
 
+        self.init_context(ctx=ctx, config_dir=config_dir)
         self.init_logging()
-        self.init_permissions()
 
+        if self.config.errors:
+            return self.log_config_issues()
+
+        self.init_permissions()
         self.expose_plugin_vars()
 
         if full_init:
@@ -330,8 +334,10 @@ class Ctl(object):
         Apply python logging config and create `log` and `usage_log`
         properties
         """
+
         # allow setting up python logging from ctl config
         set_pylogger_config(self.ctx.config.get_nested("ctl", "log"))
+
         # instantiate logger
         self.log = Log("ctl")
         self.usage_log = Log("usage")
diff --git a/src/ctl/plugins/tmp b/src/ctl/plugins/tmp
new file mode 100644
index 0000000..41bd689
--- /dev/null
+++ b/src/ctl/plugins/tmp
@@ -0,0 +1,57 @@
+plugins:
+
+  # plugin that controls the repo we want to install from
+  - type: git
+    name: git_fullctl
+    config:
+      repo_url: git@git.20c.com:fullctl/www.fullctl.com
+
+
+  # copy files 
+  - type: copy 
+    name: copy_fullctl
+    config:
+      src: {{tmp}}/git@git.20c.com:fullctl/www.fullctl.com
+      dest: {{tmp}}/builds/www.fullctl.com/{{tag}}
+      walk_dirs:
+        - {{src}}/fullctl/website
+        - {{src}}/fullctl/ixportal
+
+
+  # template files
+  - type: template
+    name: template_fullctl
+    config:
+      src: {{tmp}}/git@git.20c.com:fullctl/www.fullctl.com
+      dest: {{tmp}}/builds/www.fullctl.com/{{tag}}/fullctl
+      walk_dirs:
+        - {{src}}Ctl/fullctl/templates
+      vars:
+        - {{src}}/Ctl/fullctl/{{env}}.yaml  
+          
+
+  # rsync
+  - type: rsync
+    name: rsync_fullctl
+    config:
+      gateways:
+        beta:
+          - host: 127.0.0.1
+            user: fullctl
+
+  # run installation sequence
+  - type: chain
+    name: facsimile
+    config:
+      chains:
+        beta:
+          # stage `git` - checks out the tag passed in the cli
+          - git_fullctl checkout {{tag}}
+          # stage `copy` - copy project files
+          - copy_fullctl beta {{tag}}
+          # stage `template` - build template files
+          - template_fullctl beta {{tag}}
+          # stage `rsync`
+          - rsync_fullctl beta {{tmp}}/builds/www.fullctl.com/{{tag}}
+
+
diff --git a/src/ctl/util/versioning.py b/src/ctl/util/versioning.py
index 22bdb09..23e1390 100644
--- a/src/ctl/util/versioning.py
+++ b/src/ctl/util/versioning.py
@@ -1,5 +1,4 @@
 def version_tuple(version):
-    print("VERSION", version)
     """ Returns a tuple from version string """
     return tuple(version.split("."))
 
@@ -9,27 +8,35 @@ def version_string(version):
     return ".".join(["{}".format(v) for v in version])
 
 
-def validate_semantic(version):
+def validate_semantic(version, pad=0):
     if not isinstance(version, (list, tuple)):
         version = version_tuple(version)
 
-    try:
-        major, minor, patch, dev = version
-    except ValueError:
-        major, minor, patch = version
+    parts = len(version)
+
+    if parts < 1:
+        raise ValueError("Semantic version needs to contain at least a major version")
+    if parts > 4:
+        raise ValueError("Semantic version can not contain more than 4 parts")
+
+    if parts < pad:
+        version = tuple(list(version) + [0 for i in range(0, pad - parts)])
 
     return tuple([int(n) for n in version])
 
 
 def bump_semantic(version, segment):
-    version = list(validate_semantic(version))
     if segment == "major":
+        version = list(validate_semantic(version))
         return (version[0] + 1, 0, 0)
     elif segment == "minor":
+        version = list(validate_semantic(version, pad=2))
         return (version[0], version[1] + 1, 0)
     elif segment == "patch":
+        version = list(validate_semantic(version, pad=3))
         return (version[0], version[1], version[2] + 1)
     elif segment == "dev":
+        version = list(validate_semantic(version, pad=4))
         try:
             return (version[0], version[1], version[2], version[3] + 1)
         except IndexError:

Better error handling for config errors outside of `plugins`
Example: having a schema error in `permissions` exits ctl with traceback that's not very telling as to what is failing

reproduce:

```
permissions:
  namespace: ctl
  permission: crud
```
diff --git a/tests/test_plugin_version.py b/tests/test_plugin_version.py
index 6745c78..4b9617a 100644
--- a/tests/test_plugin_version.py
+++ b/tests/test_plugin_version.py
@@ -138,6 +138,30 @@ def test_bump(tmpdir, ctlr):
         plugin.bump(version="invalid", repo="dummy_repo")
 
 
+def test_bump_truncated(tmpdir, ctlr):
+    plugin, dummy_repo = instantiate(tmpdir, ctlr)
+    plugin.tag(version="1.0", repo="dummy_repo")
+
+    plugin.bump(version="minor", repo="dummy_repo")
+    assert dummy_repo.version == ("1", "1", "0")
+    assert dummy_repo._tag == "1.1.0"
+
+    plugin.tag(version="1.0", repo="dummy_repo")
+    plugin.bump(version="patch", repo="dummy_repo")
+    assert dummy_repo.version == ("1", "0", "1")
+    assert dummy_repo._tag == "1.0.1"
+
+    plugin.tag(version="2", repo="dummy_repo")
+    plugin.bump(version="patch", repo="dummy_repo")
+    assert dummy_repo.version == ("2", "0", "1")
+    assert dummy_repo._tag == "2.0.1"
+
+    plugin.tag(version="3", repo="dummy_repo")
+    plugin.bump(version="major", repo="dummy_repo")
+    assert dummy_repo.version == ("4", "0", "0")
+    assert dummy_repo._tag == "4.0.0"
+
+
 def test_execute(tmpdir, ctlr):
     plugin, dummy_repo = instantiate(tmpdir, ctlr)
     plugin.execute(op="tag", version="1.0.0", repository="dummy_repo", init=True)
diff --git a/tests/test_util_versioning.py b/tests/test_util_versioning.py
index b89df79..6624816 100644
--- a/tests/test_util_versioning.py
+++ b/tests/test_util_versioning.py
@@ -19,7 +19,7 @@ def test_version_tuple(version, string):
         ((1, 0, 0), (1, 0, 0), None),
         ("1.0.0.0", (1, 0, 0, 0), None),
         ((1, 0, 0, 0), (1, 0, 0, 0), None),
-        ("1.0", None, ValueError),
+        ("1.0", (1, 0), None),
         ("a.b.c", None, ValueError),
     ],
 )

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov
apt-get update apt-get install -y gcc
3.6
Ctl/requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
cfu==1.5.0
charset-normalizer==2.0.12
click==8.0.4
coverage==6.2
-e git+https://github.com/20c/ctl.git@be7f350f8f2d92918922d82fce0266fcd72decd2#egg=ctl
future==0.18.3
git-url-parse==1.2.2
grainy==1.8.1
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
munge==0.6.0
packaging==21.3
pbr==6.1.1
pluggy==1.0.0
pluginmgr==0.6.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
PyYAML==6.0.1
requests==2.27.1
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: ctl
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - cfu==1.5.0
      - charset-normalizer==2.0.12
      - click==8.0.4
      - coverage==6.2
      - future==0.18.3
      - git-url-parse==1.2.2
      - grainy==1.8.1
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - munge==0.6.0
      - packaging==21.3
      - pbr==6.1.1
      - pluggy==1.0.0
      - pluginmgr==0.6.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pyyaml==6.0.1
      - requests==2.27.1
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/ctl

tests/test_plugin_version.py::test_bump_truncated[standard] tests/test_util_versioning.py::test_validate_semantic[1.0-expected4-None]
tests/test_plugin_version.py::test_init tests/test_plugin_version.py::test_repository[standard] tests/test_plugin_version.py::test_tag[standard] tests/test_plugin_version.py::test_bump[standard] tests/test_plugin_version.py::test_execute[standard] tests/test_plugin_version.py::test_execute_permissions[permission_denied] tests/test_util_versioning.py::test_version_tuple[version0-1.0.0] tests/test_util_versioning.py::test_validate_semantic[1.0.0-expected0-None] tests/test_util_versioning.py::test_validate_semantic[version1-expected1-None] tests/test_util_versioning.py::test_validate_semantic[1.0.0.0-expected2-None] tests/test_util_versioning.py::test_validate_semantic[version3-expected3-None] tests/test_util_versioning.py::test_validate_semantic[a.b.c-None-ValueError] tests/test_util_versioning.py::test_bump_semantic[1.2.3.4-major-expected0] tests/test_util_versioning.py::test_bump_semantic[1.2.3-minor-expected1] tests/test_util_versioning.py::test_bump_semantic[1.2.3.4-patch-expected2] tests/test_util_versioning.py::test_bump_semantic[1.2.3.4-dev-expected3]
src/ctl/plugins/tmp Ctl/requirements.txt Ctl/Pipfile src/ctl/__init__.py src/ctl/util/versioning.py
src/ctl/plugins/tmp Ctl/requirements.txt Ctl/Pipfile src/ctl/__init__.py src/ctl/util/versioning.py