@softspark/ai-toolkit 4.30.2 → 4.30.3

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/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.30.3 - Copilot health-check compatibility (2026-09-02)
11
+
12
+ ### Fixed
13
+
14
+ - **`doctor` accepts sentence punctuation after runtime SemVer.** The official
15
+ Copilot CLI output `GitHub Copilot CLI 1.0.80.` now reports version `1.0.80`
16
+ instead of an invalid-SemVer warning.
17
+ - **Complete-token validation remains strict.** Extra version segments, invalid
18
+ prerelease identifiers, and leading-zero numeric fields are still rejected.
19
+ - **DSH cold-add timing gate is runner-safe.** The regression still proves a
20
+ cold package add can outlive the legacy scaled timeout while leaving enough
21
+ headroom for macOS CI process startup.
22
+
23
+ ### Changed
24
+
25
+ - **Test count.** Increased from 1920 to 1921 with the exact Copilot output
26
+ regression.
27
+ - **Release tags require green cross-platform branch CI.** The preparation SOP
28
+ now binds the gate to the exact release commit and requires both Ubuntu and
29
+ macOS Bats matrix jobs before creating or pushing a tag.
30
+
31
+ ---
32
+
10
33
  ## v4.30.2 - Portable Linux release gates (2026-09-02)
11
34
 
12
35
  ### Fixed
package/README.md CHANGED
@@ -6,28 +6,27 @@
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-109-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1920%20passing-success)](tests/)
10
-
11
- ## What's New in v4.30.2
12
-
13
- **v4.30.2** publishes the managed DSH and plugin-owned MCP release with Linux-portable recovery gates:
14
-
15
- - `install --local --editors dsh` emits the managed `.agents/skills` surface,
16
- while `ai-toolkit dsh install|update|doctor|uninstall --profile web` owns the
17
- exact DSH `0.1.1-rc.2`, dsh-codex `1.0.0`, dsh-orchestrator `1.0.1`, and preset
18
- lifecycle. DSH remains explicit-only and never handles vendor credentials.
19
- - Plugin packs can ship MCP templates and native rules for Claude, Codex, Cursor,
20
- and Gemini. Two localhost RAG templates are included, bringing the built-in MCP
21
- catalogue to 28.
22
- - Cross-file plugin operations and DSH package mutations now use ownership CAS,
23
- pinned paths, process-tree teardown, durable recovery gates, and conservative
24
- rollback that preserves concurrent or user-authored data.
25
- - Recovery verification and interrupted preset relocation now emit deterministic,
26
- non-empty diagnostics on both macOS and Linux. File-mode assertions and malformed
27
- ownership diagnostics also use the same portable contract. The public `v4.30.0`
28
- and `v4.30.1` workflows stopped before npm publication; neither registry artifact
29
- was created.
30
- - Test count: 1675 -> 1920.
9
+ [![Tests](https://img.shields.io/badge/tests-1921%20passing-success)](tests/)
10
+
11
+ ## What's New in v4.30.3
12
+
13
+ **v4.30.3** corrects the post-release health check for the managed DSH and
14
+ plugin-owned MCP release:
15
+
16
+ - `ai-toolkit doctor` now extracts `1.0.80` from the official Copilot CLI output
17
+ `GitHub Copilot CLI 1.0.80.` instead of treating sentence punctuation as an
18
+ invalid SemVer suffix.
19
+ - Complete-token validation remains fail closed for malformed versions such as
20
+ `1.2.3.4`, invalid prerelease identifiers, and leading-zero numeric fields.
21
+ - The DSH cold-add regression keeps its legacy-timeout coverage with macOS CI
22
+ process-startup headroom, removing the release-blocking timing flake.
23
+ - Release tags now require green Ubuntu and macOS branch CI for the exact commit
24
+ before the publish workflow can start.
25
+ - The release retains the explicit DSH lifecycle, plugin-owned MCP/rules, portable
26
+ recovery gates, and the exact DSH package set published in v4.30.2. Test count:
27
+ 1920 -> 1921.
28
+
29
+ See [CHANGELOG.md](CHANGELOG.md) for full history.
31
30
 
32
31
  ## Table of Contents
33
32
 
@@ -3,7 +3,7 @@
3
3
  "name": "ai-toolkit",
4
4
  "displayName": "AI Toolkit",
5
5
  "description": "Professional-grade engineering skills, agents, rules, and lifecycle guardrails for Claude Code, Claude Chat, and Cowork.",
6
- "version": "4.30.2",
6
+ "version": "4.30.3",
7
7
  "author": {
8
8
  "name": "SoftSpark",
9
9
  "url": "https://github.com/softspark"
@@ -3,10 +3,10 @@ title: "SOP: Release Preparation"
3
3
  category: procedures
4
4
  service: ai-toolkit
5
5
  tags: [sop, release, version, publish, changelog, semver, provenance, sarif, ecosystem, shellcheck]
6
- version: "1.14.0"
6
+ version: "1.15.0"
7
7
  created: "2026-04-10"
8
- last_updated: "2026-08-06"
9
- description: "Step-by-step checklist for preparing a new ai-toolkit release — ecosystem-sync drift check, version sync, changelog, artifact regeneration, validation, and tagging. Run BEFORE every git tag. Includes mandatory Provenance, SARIF, and checksum-pin checks added in v2.8.0, the single-run npm test discipline added in v1.8.0, the ecosystem-sync gate added in v1.9.0, the registry-vs-generators drift gate added in v1.10.0, the mandatory pre-tag ShellCheck gate added in v1.11.0 (publish.yml does not run ShellCheck, so a hook lint failure can publish while reddening main CI see the v4.5.1 postmortem in Phase 5), the pre-push tag assertions added in v1.12.0 after v4.19.0 was tagged on the wrong commit (Phase 7), and the licensing gate added in v1.13.0 with the move to Apache-2.0 (Phase 5c)."
8
+ last_updated: "2026-09-02"
9
+ description: "Step-by-step checklist for preparing a new ai-toolkit release — ecosystem-sync drift check, version sync, changelog, artifact regeneration, validation, branch CI, and tagging. Run BEFORE every git tag. Includes mandatory Provenance, SARIF, checksum-pin, ShellCheck, licensing, exact-tag assertions, and a green Ubuntu/macOS branch-CI gate before any release tag is created."
10
10
  ---
11
11
 
12
12
  # SOP: Release Preparation
@@ -68,16 +68,38 @@ python3 scripts/ecosystem_doctor.py --offline --check || { echo "STALE ecosystem
68
68
  # 5c. Licensing gate — SPDX headers, LICENSE, NOTICE, manifest consistency
69
69
  npx bats tests/test_licensing.bats || { echo "LICENSING GATE FAILED"; exit 1; }
70
70
 
71
- # 6. Commit + tag + push
71
+ # 6. Commit, push branch, and wait for the exact commit's full CI
72
72
  git add -A && git commit -m "chore: release vX.Y.Z"
73
- git tag vX.Y.Z
74
-
75
- # 6a. Assert the tag before pushing it (v4.19.0 postmortem, Phase 7)
76
- test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)" || { echo "FAIL: tag not on HEAD"; exit 1; }
73
+ RELEASE_SHA=$(git rev-parse HEAD)
74
+ git push origin main || { echo "FAIL: release commit push failed"; exit 1; }
75
+ RUN_ID=""
76
+ for ATTEMPT in 1 2 3 4 5; do
77
+ RUN_ID=$(gh run list --workflow ci.yml --event push --commit "$RELEASE_SHA" \
78
+ --limit 1 --json databaseId --jq '.[0].databaseId')
79
+ [ -n "$RUN_ID" ] && break
80
+ echo "Waiting for CI run registration ($ATTEMPT/5)" >&2
81
+ [ "$ATTEMPT" -eq 5 ] || sleep 60
82
+ done
83
+ test -n "$RUN_ID" || { echo "FAIL: CI run not found for release commit"; exit 1; }
84
+ gh run watch "$RUN_ID" --exit-status \
85
+ || { echo "FAIL: release commit CI failed"; exit 1; }
86
+ for OS in ubuntu-latest macos-latest; do
87
+ gh run view "$RUN_ID" --json jobs \
88
+ --jq ".jobs[] | select(.name == \"Bats test suite ($OS)\") | .conclusion" \
89
+ | grep -qx success || { echo "FAIL: $OS test job not green"; exit 1; }
90
+ done
91
+ test "$(git rev-parse HEAD)" = "$RELEASE_SHA" || { echo "FAIL: local HEAD changed during CI"; exit 1; }
92
+ git fetch origin main || { echo "FAIL: origin/main refresh failed"; exit 1; }
93
+ test "$(git rev-parse origin/main)" = "$RELEASE_SHA" || { echo "FAIL: origin/main changed during CI"; exit 1; }
94
+
95
+ # 6a. Only green branch CI authorizes tag creation
96
+ git tag vX.Y.Z "$RELEASE_SHA"
97
+
98
+ # 6b. Assert the tag before pushing it (v4.19.0 postmortem, Phase 7)
99
+ test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA" || { echo "FAIL: tag not on tested release commit"; exit 1; }
77
100
  git show --no-patch --format=%s vX.Y.Z | grep -qx "chore: release vX.Y.Z" || { echo "FAIL: tag not on release commit"; exit 1; }
78
101
 
79
- # 6b. Branch first, then the single tag by full ref. Never --tags.
80
- git push origin main
102
+ # 6c. Push the single tag by full ref. Never --tags.
81
103
  git push origin refs/tags/vX.Y.Z
82
104
  ```
83
105
 
@@ -493,23 +515,60 @@ git commit -m "chore: release vX.Y.Z"
493
515
 
494
516
  ---
495
517
 
496
- ## Phase 7: Tag and Push
518
+ ## Phase 7: Push Branch, Verify CI, Tag, and Push Tag
497
519
 
498
520
  ```bash
499
- git tag vX.Y.Z
521
+ RELEASE_SHA=$(git rev-parse HEAD)
522
+ git push origin main || { echo "FAIL: release commit push failed"; exit 1; }
523
+
524
+ # Bind the gate to the exact release commit. GitHub run registration is
525
+ # asynchronous, so retry at most five times and log every attempt.
526
+ RUN_ID=""
527
+ for ATTEMPT in 1 2 3 4 5; do
528
+ RUN_ID=$(gh run list --workflow ci.yml --event push --commit "$RELEASE_SHA" \
529
+ --limit 1 --json databaseId --jq '.[0].databaseId')
530
+ [ -n "$RUN_ID" ] && break
531
+ echo "Waiting for CI run registration ($ATTEMPT/5)" >&2
532
+ [ "$ATTEMPT" -eq 5 ] || sleep 60
533
+ done
534
+ test -n "$RUN_ID" || { echo "FAIL: CI run not found for release commit"; exit 1; }
535
+ gh run watch "$RUN_ID" --exit-status \
536
+ || { echo "FAIL: release commit CI failed"; exit 1; }
537
+
538
+ # publish.yml is Ubuntu-only. Require both full CI matrix jobs before creating
539
+ # the tag so a green publish job cannot bypass a red macOS test.
540
+ for OS in ubuntu-latest macos-latest; do
541
+ gh run view "$RUN_ID" --json jobs \
542
+ --jq ".jobs[] | select(.name == \"Bats test suite ($OS)\") | .conclusion" \
543
+ | grep -qx success || { echo "FAIL: $OS test job not green"; exit 1; }
544
+ done
545
+
546
+ # Refuse to tag if either local or remote main moved while CI was running.
547
+ test "$(git rev-parse HEAD)" = "$RELEASE_SHA" \
548
+ || { echo "FAIL: local HEAD changed during CI"; exit 1; }
549
+ git fetch origin main || { echo "FAIL: origin/main refresh failed"; exit 1; }
550
+ test "$(git rev-parse origin/main)" = "$RELEASE_SHA" \
551
+ || { echo "FAIL: origin/main changed during CI"; exit 1; }
552
+
553
+ git tag vX.Y.Z "$RELEASE_SHA"
500
554
 
501
555
  # Assert the tag before pushing it. Both checks are one line each and both
502
556
  # have caught a real broken release.
503
- test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)" \
504
- || { echo "FAIL: tag is not on HEAD"; exit 1; }
557
+ test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA" \
558
+ || { echo "FAIL: tag is not on the tested release commit"; exit 1; }
505
559
  git show --no-patch --format=%s vX.Y.Z | grep -qx "chore: release vX.Y.Z" \
506
560
  || { echo "FAIL: tag is not on the chore: release commit"; exit 1; }
507
561
 
508
- # Push the branch, then the single release tag by its full ref.
509
- git push origin main
562
+ # Push the single release tag by its full ref.
510
563
  git push origin refs/tags/vX.Y.Z
511
564
  ```
512
565
 
566
+ **Why branch CI comes before the tag (v4.30.2 postmortem).** The publish
567
+ workflow runs only on Ubuntu and can publish while the separate macOS matrix job
568
+ is red. `v4.30.2` exposed this with a macOS-only DSH timing failure. A release
569
+ commit must therefore pass the complete Ubuntu/macOS branch workflow before its
570
+ tag exists; a successful publish workflow is not a substitute for green CI.
571
+
513
572
  **Why the assertions (v4.19.0 postmortem).** v4.19.0 was tagged on a commit
514
573
  that contained only a KB document and still carried `package.json` version
515
574
  `4.18.0`; the actual release sat in the commit above it under a recycled
@@ -580,7 +639,9 @@ git push origin --delete vX.Y.Z
580
639
  | 15a | Licensing gate | `npx bats tests/test_licensing.bats` | 7/7 — SPDX headers, LICENSE, NOTICE, npm `files`, manifest consistency |
581
640
  | 16 | Tests | `git add -A kb/` if the KB changed, then `npm test` | All pass |
582
641
  | 17 | Commit | `git commit` | Clean working tree |
583
- | 18 | Tag | `git tag vX.Y.Z` | Tag exists |
584
- | 18a | Tag is on HEAD | `test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)"` | Exit 0 |
585
- | 18b | Tag is on the release commit | `git show --no-patch --format=%s vX.Y.Z` | Reads `chore: release vX.Y.Z` |
586
- | 19 | Push branch, then the single tag | `git push origin main && git push origin refs/tags/vX.Y.Z` | CI triggered with `id-token: write`. Never `--tags`. |
642
+ | 18 | Push branch | `git push origin main` | Exact release commit is on `origin/main` |
643
+ | 18a | Full branch CI | `gh run watch "$RUN_ID" --exit-status` plus matrix job assertions | Ubuntu and macOS Bats jobs both conclude `success` |
644
+ | 19 | Tag | `git tag vX.Y.Z` | Tag exists only after green branch CI |
645
+ | 19a | Tag is on tested SHA | `test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA"` | Exit 0 |
646
+ | 19b | Tag is on the release commit | `git show --no-patch --format=%s vX.Y.Z` | Reads `chore: release vX.Y.Z` |
647
+ | 20 | Push the single tag | `git push origin refs/tags/vX.Y.Z` | Publish CI triggered with `id-token: write`. Never `--tags`. |
package/llms-full.txt CHANGED
@@ -8032,10 +8032,10 @@ title: "SOP: Release Preparation"
8032
8032
  category: procedures
8033
8033
  service: ai-toolkit
8034
8034
  tags: [sop, release, version, publish, changelog, semver, provenance, sarif, ecosystem, shellcheck]
8035
- version: "1.14.0"
8035
+ version: "1.15.0"
8036
8036
  created: "2026-04-10"
8037
- last_updated: "2026-08-06"
8038
- description: "Step-by-step checklist for preparing a new ai-toolkit release — ecosystem-sync drift check, version sync, changelog, artifact regeneration, validation, and tagging. Run BEFORE every git tag. Includes mandatory Provenance, SARIF, and checksum-pin checks added in v2.8.0, the single-run npm test discipline added in v1.8.0, the ecosystem-sync gate added in v1.9.0, the registry-vs-generators drift gate added in v1.10.0, the mandatory pre-tag ShellCheck gate added in v1.11.0 (publish.yml does not run ShellCheck, so a hook lint failure can publish while reddening main CI see the v4.5.1 postmortem in Phase 5), the pre-push tag assertions added in v1.12.0 after v4.19.0 was tagged on the wrong commit (Phase 7), and the licensing gate added in v1.13.0 with the move to Apache-2.0 (Phase 5c)."
8037
+ last_updated: "2026-09-02"
8038
+ description: "Step-by-step checklist for preparing a new ai-toolkit release — ecosystem-sync drift check, version sync, changelog, artifact regeneration, validation, branch CI, and tagging. Run BEFORE every git tag. Includes mandatory Provenance, SARIF, checksum-pin, ShellCheck, licensing, exact-tag assertions, and a green Ubuntu/macOS branch-CI gate before any release tag is created."
8039
8039
  ---
8040
8040
 
8041
8041
  # SOP: Release Preparation
@@ -8097,16 +8097,38 @@ python3 scripts/ecosystem_doctor.py --offline --check || { echo "STALE ecosystem
8097
8097
  # 5c. Licensing gate — SPDX headers, LICENSE, NOTICE, manifest consistency
8098
8098
  npx bats tests/test_licensing.bats || { echo "LICENSING GATE FAILED"; exit 1; }
8099
8099
 
8100
- # 6. Commit + tag + push
8100
+ # 6. Commit, push branch, and wait for the exact commit's full CI
8101
8101
  git add -A && git commit -m "chore: release vX.Y.Z"
8102
- git tag vX.Y.Z
8102
+ RELEASE_SHA=$(git rev-parse HEAD)
8103
+ git push origin main || { echo "FAIL: release commit push failed"; exit 1; }
8104
+ RUN_ID=""
8105
+ for ATTEMPT in 1 2 3 4 5; do
8106
+ RUN_ID=$(gh run list --workflow ci.yml --event push --commit "$RELEASE_SHA" \
8107
+ --limit 1 --json databaseId --jq '.[0].databaseId')
8108
+ [ -n "$RUN_ID" ] && break
8109
+ echo "Waiting for CI run registration ($ATTEMPT/5)" >&2
8110
+ [ "$ATTEMPT" -eq 5 ] || sleep 60
8111
+ done
8112
+ test -n "$RUN_ID" || { echo "FAIL: CI run not found for release commit"; exit 1; }
8113
+ gh run watch "$RUN_ID" --exit-status \
8114
+ || { echo "FAIL: release commit CI failed"; exit 1; }
8115
+ for OS in ubuntu-latest macos-latest; do
8116
+ gh run view "$RUN_ID" --json jobs \
8117
+ --jq ".jobs[] | select(.name == \"Bats test suite ($OS)\") | .conclusion" \
8118
+ | grep -qx success || { echo "FAIL: $OS test job not green"; exit 1; }
8119
+ done
8120
+ test "$(git rev-parse HEAD)" = "$RELEASE_SHA" || { echo "FAIL: local HEAD changed during CI"; exit 1; }
8121
+ git fetch origin main || { echo "FAIL: origin/main refresh failed"; exit 1; }
8122
+ test "$(git rev-parse origin/main)" = "$RELEASE_SHA" || { echo "FAIL: origin/main changed during CI"; exit 1; }
8123
+
8124
+ # 6a. Only green branch CI authorizes tag creation
8125
+ git tag vX.Y.Z "$RELEASE_SHA"
8103
8126
 
8104
- # 6a. Assert the tag before pushing it (v4.19.0 postmortem, Phase 7)
8105
- test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)" || { echo "FAIL: tag not on HEAD"; exit 1; }
8127
+ # 6b. Assert the tag before pushing it (v4.19.0 postmortem, Phase 7)
8128
+ test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA" || { echo "FAIL: tag not on tested release commit"; exit 1; }
8106
8129
  git show --no-patch --format=%s vX.Y.Z | grep -qx "chore: release vX.Y.Z" || { echo "FAIL: tag not on release commit"; exit 1; }
8107
8130
 
8108
- # 6b. Branch first, then the single tag by full ref. Never --tags.
8109
- git push origin main
8131
+ # 6c. Push the single tag by full ref. Never --tags.
8110
8132
  git push origin refs/tags/vX.Y.Z
8111
8133
  ```
8112
8134
 
@@ -8522,23 +8544,60 @@ git commit -m "chore: release vX.Y.Z"
8522
8544
 
8523
8545
  ---
8524
8546
 
8525
- ## Phase 7: Tag and Push
8547
+ ## Phase 7: Push Branch, Verify CI, Tag, and Push Tag
8526
8548
 
8527
8549
  ```bash
8528
- git tag vX.Y.Z
8550
+ RELEASE_SHA=$(git rev-parse HEAD)
8551
+ git push origin main || { echo "FAIL: release commit push failed"; exit 1; }
8552
+
8553
+ # Bind the gate to the exact release commit. GitHub run registration is
8554
+ # asynchronous, so retry at most five times and log every attempt.
8555
+ RUN_ID=""
8556
+ for ATTEMPT in 1 2 3 4 5; do
8557
+ RUN_ID=$(gh run list --workflow ci.yml --event push --commit "$RELEASE_SHA" \
8558
+ --limit 1 --json databaseId --jq '.[0].databaseId')
8559
+ [ -n "$RUN_ID" ] && break
8560
+ echo "Waiting for CI run registration ($ATTEMPT/5)" >&2
8561
+ [ "$ATTEMPT" -eq 5 ] || sleep 60
8562
+ done
8563
+ test -n "$RUN_ID" || { echo "FAIL: CI run not found for release commit"; exit 1; }
8564
+ gh run watch "$RUN_ID" --exit-status \
8565
+ || { echo "FAIL: release commit CI failed"; exit 1; }
8566
+
8567
+ # publish.yml is Ubuntu-only. Require both full CI matrix jobs before creating
8568
+ # the tag so a green publish job cannot bypass a red macOS test.
8569
+ for OS in ubuntu-latest macos-latest; do
8570
+ gh run view "$RUN_ID" --json jobs \
8571
+ --jq ".jobs[] | select(.name == \"Bats test suite ($OS)\") | .conclusion" \
8572
+ | grep -qx success || { echo "FAIL: $OS test job not green"; exit 1; }
8573
+ done
8574
+
8575
+ # Refuse to tag if either local or remote main moved while CI was running.
8576
+ test "$(git rev-parse HEAD)" = "$RELEASE_SHA" \
8577
+ || { echo "FAIL: local HEAD changed during CI"; exit 1; }
8578
+ git fetch origin main || { echo "FAIL: origin/main refresh failed"; exit 1; }
8579
+ test "$(git rev-parse origin/main)" = "$RELEASE_SHA" \
8580
+ || { echo "FAIL: origin/main changed during CI"; exit 1; }
8581
+
8582
+ git tag vX.Y.Z "$RELEASE_SHA"
8529
8583
 
8530
8584
  # Assert the tag before pushing it. Both checks are one line each and both
8531
8585
  # have caught a real broken release.
8532
- test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)" \
8533
- || { echo "FAIL: tag is not on HEAD"; exit 1; }
8586
+ test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA" \
8587
+ || { echo "FAIL: tag is not on the tested release commit"; exit 1; }
8534
8588
  git show --no-patch --format=%s vX.Y.Z | grep -qx "chore: release vX.Y.Z" \
8535
8589
  || { echo "FAIL: tag is not on the chore: release commit"; exit 1; }
8536
8590
 
8537
- # Push the branch, then the single release tag by its full ref.
8538
- git push origin main
8591
+ # Push the single release tag by its full ref.
8539
8592
  git push origin refs/tags/vX.Y.Z
8540
8593
  ```
8541
8594
 
8595
+ **Why branch CI comes before the tag (v4.30.2 postmortem).** The publish
8596
+ workflow runs only on Ubuntu and can publish while the separate macOS matrix job
8597
+ is red. `v4.30.2` exposed this with a macOS-only DSH timing failure. A release
8598
+ commit must therefore pass the complete Ubuntu/macOS branch workflow before its
8599
+ tag exists; a successful publish workflow is not a substitute for green CI.
8600
+
8542
8601
  **Why the assertions (v4.19.0 postmortem).** v4.19.0 was tagged on a commit
8543
8602
  that contained only a KB document and still carried `package.json` version
8544
8603
  `4.18.0`; the actual release sat in the commit above it under a recycled
@@ -8609,10 +8668,12 @@ git push origin --delete vX.Y.Z
8609
8668
  | 15a | Licensing gate | `npx bats tests/test_licensing.bats` | 7/7 — SPDX headers, LICENSE, NOTICE, npm `files`, manifest consistency |
8610
8669
  | 16 | Tests | `git add -A kb/` if the KB changed, then `npm test` | All pass |
8611
8670
  | 17 | Commit | `git commit` | Clean working tree |
8612
- | 18 | Tag | `git tag vX.Y.Z` | Tag exists |
8613
- | 18a | Tag is on HEAD | `test "$(git rev-parse vX.Y.Z)" = "$(git rev-parse HEAD)"` | Exit 0 |
8614
- | 18b | Tag is on the release commit | `git show --no-patch --format=%s vX.Y.Z` | Reads `chore: release vX.Y.Z` |
8615
- | 19 | Push branch, then the single tag | `git push origin main && git push origin refs/tags/vX.Y.Z` | CI triggered with `id-token: write`. Never `--tags`. |
8671
+ | 18 | Push branch | `git push origin main` | Exact release commit is on `origin/main` |
8672
+ | 18a | Full branch CI | `gh run watch "$RUN_ID" --exit-status` plus matrix job assertions | Ubuntu and macOS Bats jobs both conclude `success` |
8673
+ | 19 | Tag | `git tag vX.Y.Z` | Tag exists only after green branch CI |
8674
+ | 19a | Tag is on tested SHA | `test "$(git rev-parse vX.Y.Z)" = "$RELEASE_SHA"` | Exit 0 |
8675
+ | 19b | Tag is on the release commit | `git show --no-patch --format=%s vX.Y.Z` | Reads `chore: release vX.Y.Z` |
8676
+ | 20 | Push the single tag | `git push origin refs/tags/vX.Y.Z` | Publish CI triggered with `id-token: write`. Never `--tags`. |
8616
8677
 
8617
8678
  ---
8618
8679
 
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.30.2",
2
+ "version": "4.30.3",
3
3
  "components": {
4
4
  "agents": {
5
5
  "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "4.30.2",
3
+ "version": "4.30.3",
4
4
  "description": "AI coding toolkit: 109 skills, 44 agents, 14 developer-tool integrations, recoverable native tool-output filtering, Claude Chat/Cowork export, safety constitution, SARIF audit, and signed npm provenance.",
5
5
  "keywords": [
6
6
  "claude",
package/scripts/doctor.py CHANGED
@@ -124,7 +124,7 @@ SEMVER_OUTPUT = re.compile(
124
124
  rf"(?:\.{_SEMVER_PRERELEASE_IDENTIFIER})*)?"
125
125
  rf"(?:\+{_SEMVER_BUILD_IDENTIFIER}"
126
126
  rf"(?:\.{_SEMVER_BUILD_IDENTIFIER})*)?)"
127
- r"(?![0-9A-Za-z.+-])"
127
+ r"(?![0-9A-Za-z+-]|\.(?!\s|$))"
128
128
  )
129
129
  VERSION_LIKE_OUTPUT = re.compile(r"(?<![0-9])[0-9]+\.[0-9]+\.[^\s,;()]+")
130
130