From d47b88ac15cbe58e157513e0e673ba519dca86a1 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 10:29:34 -0400 Subject: [PATCH 1/8] test signing From 232f30526aa8b1be998e3397279a7a01a08da8d6 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 10:45:25 -0400 Subject: [PATCH 2/8] Document how to clean up merged feature branches --- docs/branching.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/branching.md b/docs/branching.md index 5f3f2c6..003d378 100644 --- a/docs/branching.md +++ b/docs/branching.md @@ -87,3 +87,33 @@ When working on multiple features simultaneously, **sequence tasks that touch ov This is especially important for tasks that involve renaming, restructuring, or updating cross-references across skills. These changes tend to be broad in scope (touching many files) even though the individual edits are small. Running `sync.sh` amplifies the problem further: a one-line edit in a source skill becomes changes across every adapter directory (`.claude/`, `.cursor/`, `.agents/`, `.gemini/`), multiplying the conflict surface. If you do end up with parallel branches that conflict, the cleanest resolution is usually to re-apply the smaller change on a fresh branch off the updated integration branch, rather than resolving conflicts file-by-file. The mechanical nature of most cross-cutting changes (find-and-replace + `sync.sh`) makes this fast and reliable. + +## Cleaning up merged branches + +Branch cleanup is optional housekeeping. Some teams keep every branch forever for history, others prune after each sprint. Code Cannon has no opinion and enforces nothing — do whatever fits your workflow. + +**Easiest baseline:** enable GitHub's *Settings → General → "Automatically delete head branches"*. Every merged PR then deletes its remote branch automatically. This handles the remote side with zero ongoing effort. + +**Manual cleanup:** the following one-liners are safe to re-run any time. Replace `dev` with your own `BRANCH_DEV` if different. + +Prune local refs to branches that no longer exist on the remote: + +```bash +git fetch --prune +``` + +Delete local branches already merged into the integration branch (excludes `main`, `dev`, `test`, and the current branch; uses `-d` so git refuses to delete anything unmerged): + +```bash +git branch --merged dev | grep -vE '^\*|^\s*(main|dev|test)$' | xargs -n1 git branch -d +``` + +Delete remote branches already merged (for users who don't enable GitHub's auto-delete): + +```bash +git branch -r --merged origin/dev | grep -vE 'origin/(HEAD|main|dev|test)' | sed 's|origin/||' | xargs -n1 git push origin --delete +``` + +A natural time to run cleanup is right after `/deploy`, once the integration branch has been promoted to production — but that's personal preference, not a Code Cannon rule. Run it whenever it feels useful. + +**Only delete branches that have been merged into the integration branch.** Never prune by age, staleness, or "looks abandoned" heuristics — an old branch may be someone's in-progress work, and `git branch -D` (or a force-push delete) will destroy it silently. The `--merged` filter is what makes the commands above safe; don't swap it out for a time-based one. From 2de05e0abb66637fbfaaca7b2709a52497a7a5b5 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 11:11:46 -0400 Subject: [PATCH 3/8] Clarify branch cleanup one-liner applies to custom branch names --- docs/branching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/branching.md b/docs/branching.md index 003d378..bc06611 100644 --- a/docs/branching.md +++ b/docs/branching.md @@ -94,7 +94,7 @@ Branch cleanup is optional housekeeping. Some teams keep every branch forever fo **Easiest baseline:** enable GitHub's *Settings → General → "Automatically delete head branches"*. Every merged PR then deletes its remote branch automatically. This handles the remote side with zero ongoing effort. -**Manual cleanup:** the following one-liners are safe to re-run any time. Replace `dev` with your own `BRANCH_DEV` if different. +**Manual cleanup:** the following one-liners are safe to re-run any time. They assume the default branch names `main`, `dev`, and `test` — if yours differ, substitute your own names everywhere they appear below (both in the `git branch --merged` argument and in the `grep` exclusion list). Prune local refs to branches that no longer exist on the remote: From 15afedb6ca7a53bd33ce916175709c46516cdbf3 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 11:14:50 -0400 Subject: [PATCH 4/8] Bump version to 0.3.7 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 449d7e7..0f82685 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.6 +0.3.7 From 32ef22724e46ed857f78e01944a97f79d03b19e9 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 11:26:30 -0400 Subject: [PATCH 5/8] Force explicit signing in Makefile version bump targets --- Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index d1e56e2..f2f11d9 100644 --- a/Makefile +++ b/Makefile @@ -54,8 +54,8 @@ bump-patch: new="$$major.$$minor.$$((patch + 1))"; \ echo $$new > VERSION; \ git add VERSION; \ - git commit -m "Bump version to $$new"; \ - git tag v$$new + git commit -S -m "Bump version to $$new"; \ + git tag -s v$$new -m "v$$new" # Bump the minor segment (X.Y.Z → X.Y+1.0), commit, and tag. bump-minor: @@ -65,8 +65,8 @@ bump-minor: new="$$major.$$((minor + 1)).0"; \ echo $$new > VERSION; \ git add VERSION; \ - git commit -m "Bump version to $$new"; \ - git tag v$$new + git commit -S -m "Bump version to $$new"; \ + git tag -s v$$new -m "v$$new" # Bump the major segment (X.Y.Z → X+1.0.0), commit, and tag. bump-major: @@ -75,8 +75,8 @@ bump-major: new="$$((major + 1)).0.0"; \ echo $$new > VERSION; \ git add VERSION; \ - git commit -m "Bump version to $$new"; \ - git tag v$$new + git commit -S -m "Bump version to $$new"; \ + git tag -s v$$new -m "v$$new" # Set an explicit version. Usage: make set-version V=1.2.3 set-version: @@ -85,8 +85,8 @@ ifndef V endif echo $(V) > VERSION git add VERSION - git commit -m "Bump version to $(V)" - git tag v$(V) + git commit -S -m "Bump version to $(V)" + git tag -s v$(V) -m "v$(V)" # Push the integration branch for preview/testing. deploy-preview: From fcdd6196cf02345623d77284af336644d3a390e2 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 11:28:58 -0400 Subject: [PATCH 6/8] Bump version to 0.3.8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0f82685..6678432 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.7 +0.3.8 From 48680c5f959cbf47a80e339df84590ff78eae100 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 12:28:43 -0400 Subject: [PATCH 7/8] Fix sync.sh YAML dequoting and default TICKET_LABEL_CREATION_ALLOWED --- sync.sh | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/sync.sh b/sync.sh index 7d60df8..253ddb8 100755 --- a/sync.sh +++ b/sync.sh @@ -37,6 +37,22 @@ def first_line_has_sync_marker(first_line): # We parse a strict subset of YAML: top-level keys, one-level-deep key:value # pairs, and simple lists. No multi-line scalars, no anchors, no complex types. +def _dequote(value): + """Strip a single matching pair of surrounding quotes. + + Double-quoted values decode the YAML escapes we actually use: \\" → " and \\\\ → \\. + Single-quoted values are returned verbatim (no escape processing). + Unquoted values are returned as-is. + """ + if len(value) >= 2 and value[0] == '"' and value[-1] == '"': + inner = value[1:-1] + # Decode \\ first to a placeholder so \" decoding doesn't see escaped backslashes + return inner.replace('\\\\', '\x00').replace('\\"', '"').replace('\x00', '\\') + if len(value) >= 2 and value[0] == "'" and value[-1] == "'": + return value[1:-1] + return value + + def parse_yaml_simple(text): """Parse a simple flat YAML structure into a dict.""" result = {} @@ -55,7 +71,7 @@ def parse_yaml_simple(text): if ':' in stripped: key, _, value = stripped.partition(':') key = key.strip() - value = value.strip().strip('"').strip("'") + value = _dequote(value.strip()) current_key = key if value: result[key] = value @@ -63,14 +79,14 @@ def parse_yaml_simple(text): result[key] = {} elif indent >= 2 and current_key is not None: if stripped.startswith('- '): - value = stripped[2:].strip().strip('"').strip("'") + value = _dequote(stripped[2:].strip()) if not isinstance(result.get(current_key), list): result[current_key] = [] result[current_key].append(value) elif ':' in stripped and isinstance(result.get(current_key), dict): key, _, value = stripped.partition(':') key = key.strip() - value = value.strip().strip('"').strip("'") + value = _dequote(value.strip()) result[current_key][key] = value return result @@ -86,12 +102,12 @@ def parse_frontmatter(text): for line in fm_text.splitlines(): if ':' in line: key, _, value = line.partition(':') - raw_val = value.strip().strip('"').strip("'") + stripped_val = value.strip() # Handle YAML lists on a single line: [a, b, c] - if raw_val.startswith('[') and raw_val.endswith(']'): - fm[key.strip()] = [v.strip().strip('"') for v in raw_val[1:-1].split(',')] + if stripped_val.startswith('[') and stripped_val.endswith(']'): + fm[key.strip()] = [_dequote(v.strip()) for v in stripped_val[1:-1].split(',')] else: - fm[key.strip()] = raw_val + fm[key.strip()] = _dequote(stripped_val) return fm, body.strip() @@ -401,6 +417,10 @@ def main(): adapters_list = raw_config.get('adapters', []) project_config = raw_config.get('config', {}) + # Default for optional placeholders that the template ships commented out + # but skills reference unconditionally. Matches the documented default. + project_config.setdefault('TICKET_LABEL_CREATION_ALLOWED', 'false') + if not adapters_list: print("Error: no adapters specified in config. Add 'adapters: [claude]' to .codecannon.yaml") sys.exit(1) From 6415e3df3abcf803fe8de16bcd559b625d331f14 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 8 Apr 2026 12:32:00 -0400 Subject: [PATCH 8/8] Bump version to 0.3.9 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 6678432..940ac09 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.8 +0.3.9