@keelcore/standards 0.9.7 → 0.10.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 +37 -26
- package/governance/ai/conduct.md +17 -26
- package/governance/api-management.md +91 -102
- package/governance/arb.md +55 -62
- package/governance/bash.md +117 -46
- package/governance/ci.md +165 -144
- package/governance/coding.md +89 -78
- package/governance/observability.md +77 -86
- package/governance/platform.md +81 -87
- package/governance/release-policy.md +126 -96
- package/governance/runtime.md +87 -95
- package/governance/security.md +77 -85
- package/governance/testing.md +66 -20
- package/package.json +1 -1
package/governance/platform.md
CHANGED
|
@@ -1,156 +1,150 @@
|
|
|
1
1
|
# Platform Architecture Standards
|
|
2
2
|
|
|
3
|
-
These rules govern platform-level decisions across networking, DNS, TLS, WAF, and traffic management.
|
|
4
|
-
|
|
3
|
+
These rules govern platform-level decisions across networking, DNS, TLS, WAF, and traffic management. They are
|
|
4
|
+
non-negotiable unless explicitly superseded by a signed ADR.
|
|
5
5
|
|
|
6
|
-
**Maturity:** Required
|
|
7
|
-
**Version:** 1.0.0
|
|
8
|
-
**Last Reviewed:** 2026-03-09
|
|
6
|
+
**Maturity:** Required **Version:** 1.0.0 **Last Reviewed:** 2026-03-09
|
|
9
7
|
|
|
10
8
|
## Governance Process
|
|
11
9
|
|
|
12
10
|
### Decision-Making (DACI)
|
|
13
11
|
|
|
14
|
-
1. All platform architecture decisions use DACI: one Driver, one Approver (Architecture Review Board
|
|
15
|
-
|
|
12
|
+
1. All platform architecture decisions use DACI: one Driver, one Approver (Architecture Review Board chair or delegate),
|
|
13
|
+
Consulted stakeholders, and Informed parties.
|
|
16
14
|
2. The Driver authors the RFC and owns shepherding it through review.
|
|
17
15
|
3. The Approver has final authority; no decision is binding without Approver sign-off.
|
|
18
16
|
4. Decisions that affect more than one service domain require ARB review before implementation.
|
|
19
17
|
|
|
20
18
|
### RFC Process
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
1. Any change to platform standards, vendor selection, or network topology requires an RFC.
|
|
21
|
+
2. RFC template: problem statement, proposed solution, alternatives considered, rollout plan, rollback plan.
|
|
22
|
+
3. RFC comment period is a minimum of five business days for standard changes; urgent security patches may compress to
|
|
23
|
+
24 hours with ARB chair notification.
|
|
24
|
+
4. Accepted RFCs are archived in `docs/rfc/` with status and the date of acceptance.
|
|
27
25
|
|
|
28
26
|
### Architecture Review Board (ARB)
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
1. ARB meets on a defined cadence (minimum monthly); decisions and attendance are published to the engineering wiki
|
|
29
|
+
within five business days of each meeting.
|
|
30
|
+
2. ARB publishes a versioned Architecture Standards document at least once per quarter.
|
|
31
|
+
3. Previous versions of Architecture Standards are archived and permanently accessible.
|
|
34
32
|
|
|
35
33
|
## Domain and IP
|
|
36
34
|
|
|
37
35
|
### Domain Registrar
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
1. All domains are registered through the approved corporate registrar (documented in the internal vendor registry).
|
|
38
|
+
Personal or team-owned registrar accounts are prohibited for production domains.
|
|
39
|
+
2. Domain transfers require ARB approval and a change record.
|
|
40
|
+
3. WHOIS privacy protection is enabled on all externally registered domains.
|
|
43
41
|
|
|
44
42
|
### IP Address Management
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
1. IP allocations are managed through the approved IPAM vendor (documented in the internal vendor registry).
|
|
45
|
+
2. All IP blocks are tagged with owner, environment, and region.
|
|
46
|
+
3. IPv4 and IPv6 are both required for all new public-facing services. Dual-stack is the baseline; IPv4-only services
|
|
47
|
+
are not accepted for new deployments.
|
|
48
|
+
4. Private RFC 1918 ranges are used for internal pod and node networks. Overlap with VPN or on-premises ranges must be
|
|
49
|
+
approved through the IPAM change process.
|
|
52
50
|
|
|
53
51
|
## DNS
|
|
54
52
|
|
|
55
53
|
### Zone Management
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
1. DNS zones are managed through the approved DNS vendor (documented in the internal vendor registry). Manual zone edits
|
|
56
|
+
via vendor UI are prohibited except in a documented break-glass scenario.
|
|
57
|
+
2. All zone changes are driven from infrastructure-as-code (Terraform, Pulumi, or equivalent) in a version-controlled
|
|
58
|
+
repository. Zone state is reconciled on every deployment.
|
|
59
|
+
3. Internal and external zones are segregated. Internal zone resolvers are not exposed to the public internet.
|
|
62
60
|
|
|
63
61
|
### Required Record Types
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Internal records are never propagated to public resolvers.
|
|
63
|
+
1. Public services must expose: A, AAAA, and the appropriate CNAME or alias records.
|
|
64
|
+
2. Email domains must have SPF, DKIM, and DMARC records. DMARC policy must be `p=quarantine` or stricter in production.
|
|
65
|
+
3. Wildcard DNS records (`*.example.com`) are permitted only for edge/ingress controllers that validate per-host routing
|
|
66
|
+
internally. Wildcard records must not resolve to application pods directly.
|
|
67
|
+
4. Internal service discovery uses a dedicated internal TLD (e.g., `.internal` or cluster DNS suffix). Internal records
|
|
68
|
+
are never propagated to public resolvers.
|
|
72
69
|
|
|
73
70
|
## TLS and Certificate Management
|
|
74
71
|
|
|
75
72
|
### Certificate Issuance
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
1. TLS certificates for public endpoints are issued from an approved CA vendor (documented in the internal vendor
|
|
75
|
+
registry). Self-signed certificates are prohibited on any public-facing endpoint.
|
|
76
|
+
2. Certificate issuance and renewal must use the ACME protocol. Manual certificate downloads and uploads are prohibited
|
|
77
|
+
except in a documented break-glass scenario.
|
|
78
|
+
3. Certificates are provisioned and renewed automatically. Expiry-based alerts fire at 30 days and again at 7 days
|
|
79
|
+
before expiration.
|
|
83
80
|
|
|
84
81
|
### Mutual TLS (mTLS)
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
1. East-west traffic between services must use mTLS. Plaintext service-to-service communication is prohibited in staging
|
|
84
|
+
and production.
|
|
85
|
+
2. mTLS certificates are issued by the internal PKI (SPIFFE/SPIRE or equivalent). Leaf certificates rotate on a maximum
|
|
86
|
+
24-hour lifetime. Rotation is automatic and zero-downtime.
|
|
87
|
+
3. mTLS policy is enforced at the service mesh layer, not individually per application.
|
|
91
88
|
|
|
92
89
|
### Supported Protocols and Cipher Suites
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
1. Minimum TLS version: TLS 1.2. TLS 1.3 is required for all new integrations.
|
|
92
|
+
2. TLS 1.0 and TLS 1.1 are prohibited on all endpoints.
|
|
93
|
+
3. Cipher suites: prefer ECDHE key exchange with AES-GCM or ChaCha20-Poly1305. RC4, DES, 3DES, and export-grade ciphers
|
|
94
|
+
are prohibited. DHE suites below 2048-bit DH parameters are prohibited.
|
|
95
|
+
4. HSTS is required on all HTTPS endpoints with `max-age` of at least one year.
|
|
99
96
|
|
|
100
97
|
## Web Application Firewall (WAF) and DDoS
|
|
101
98
|
|
|
102
99
|
### WAF
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
1. All public HTTP/HTTPS ingress passes through the WAF layer before reaching application pods.
|
|
102
|
+
2. The OWASP Core Rule Set (CRS) is enforced in blocking mode. Detection-only mode is not acceptable for production.
|
|
103
|
+
Tuning exceptions require a documented rule with justification.
|
|
104
|
+
3. WAF rules are version-controlled. Rule changes follow the RFC process.
|
|
105
|
+
4. WAF logs are ingested into the central log aggregation platform with a minimum 90-day retention.
|
|
109
106
|
|
|
110
107
|
### DDoS Protection
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
1. All public endpoints are protected by volumetric DDoS mitigation at the network edge.
|
|
110
|
+
2. L7 DDoS (HTTP flood) mitigation is enforced by the WAF layer with rate limiting rules.
|
|
111
|
+
3. Incident response runbooks for DDoS events are maintained and reviewed quarterly.
|
|
115
112
|
|
|
116
113
|
## Security Headers
|
|
117
114
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
implementation details.
|
|
115
|
+
1. All HTTP responses from public endpoints must include:
|
|
116
|
+
- `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload`
|
|
117
|
+
- `X-Content-Type-Options: nosniff`
|
|
118
|
+
- `X-Frame-Options: DENY`
|
|
119
|
+
- `Content-Security-Policy` (service-specific, reviewed by security team)
|
|
120
|
+
- `Referrer-Policy: strict-origin-when-cross-origin`
|
|
121
|
+
- `Permissions-Policy` (restrictive default; expand per-service with justification)
|
|
122
|
+
2. `Server` and `X-Powered-By` response headers must be suppressed or overridden to not reveal implementation details.
|
|
127
123
|
|
|
128
124
|
## Traffic Management
|
|
129
125
|
|
|
130
126
|
### Payload Limits
|
|
131
127
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
47. Maximum response size is bounded by the service contract. Unbounded response streaming must
|
|
137
|
-
implement flow control.
|
|
128
|
+
1. Default maximum upload size: 10 MB. Services requiring larger uploads must document and configure an explicit limit;
|
|
129
|
+
unbounded uploads are prohibited.
|
|
130
|
+
2. Maximum request body size: 1 MB by default for API endpoints. Streaming endpoints must implement backpressure.
|
|
131
|
+
3. Maximum response size is bounded by the service contract. Unbounded response streaming must implement flow control.
|
|
138
132
|
|
|
139
133
|
### Rate Limiting
|
|
140
134
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
1. Global rate limiting is enforced at the edge/ingress layer.
|
|
136
|
+
2. Per-service rate limits are configured and documented in each service's runbook.
|
|
137
|
+
3. Rate limit responses use HTTP 429 with a `Retry-After` header.
|
|
138
|
+
4. Rate limit state is shared across edge replicas (e.g., Redis-backed or edge-native distributed counters). Per-replica
|
|
139
|
+
rate limiting that allows bypass via replica selection is prohibited.
|
|
146
140
|
|
|
147
141
|
### Session Management
|
|
148
142
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
143
|
+
1. Session tokens are cryptographically random with a minimum of 128 bits of entropy.
|
|
144
|
+
2. Session lifetime: configurable per service, with a maximum idle timeout of 15 minutes and a maximum absolute session
|
|
145
|
+
lifetime of 8 hours for human-facing sessions.
|
|
146
|
+
3. Sessions are invalidated server-side on logout. Client-side deletion alone is insufficient.
|
|
147
|
+
4. Session refresh tokens must be rotated on every use.
|
|
154
148
|
|
|
155
149
|
## Do Not
|
|
156
150
|
|
|
@@ -1,76 +1,131 @@
|
|
|
1
1
|
# Release Policy
|
|
2
2
|
|
|
3
|
-
**Maturity:** Required
|
|
4
|
-
**Version:** 1.0.0
|
|
5
|
-
**Last Reviewed:** 2026-03-11
|
|
3
|
+
**Maturity:** Required **Version:** 1.1.0 **Last Reviewed:** 2026-10-30
|
|
6
4
|
|
|
7
|
-
This document covers how
|
|
8
|
-
policy lives in
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
This document covers how a project that follows the keelcore standards tags and releases a version. The authoritative
|
|
6
|
+
policy lives in [`ci.md`](ci.md) under **Release Tagging**. This document is the developer-facing how-to that
|
|
7
|
+
complements that policy. It applies to every project as written; what a project may change, and where, is set out in
|
|
8
|
+
[Project Customization](#project-customization) below.
|
|
11
9
|
|
|
12
10
|
---
|
|
13
11
|
|
|
14
12
|
## Versioning Specification
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
must not contradict it.
|
|
14
|
+
Projects follow **Semantic Versioning 2.0.0** — [https://semver.org](https://semver.org). That document is the
|
|
15
|
+
canonical reference; the rules below are derived from it and must not contradict it.
|
|
19
16
|
|
|
20
17
|
Key semver.org rules in force here:
|
|
21
18
|
|
|
22
|
-
- Version numbers take the form `MAJOR.MINOR.PATCH` where each component is a
|
|
23
|
-
|
|
19
|
+
- Version numbers take the form `MAJOR.MINOR.PATCH` where each component is a non-negative integer with no leading
|
|
20
|
+
zeroes.
|
|
24
21
|
- MAJOR is incremented for incompatible API changes; MINOR and PATCH reset to 0.
|
|
25
22
|
- MINOR is incremented for new backward-compatible functionality; PATCH resets to 0.
|
|
26
23
|
- PATCH is incremented for backward-compatible bug fixes only.
|
|
27
|
-
- Major version zero (`0.y.z`) is for initial development. The public API is not
|
|
28
|
-
|
|
24
|
+
- Major version zero (`0.y.z`) is for initial development. The public API is not yet stable and anything may change at
|
|
25
|
+
any time.
|
|
29
26
|
- `1.0.0` defines the first stable public API.
|
|
30
|
-
- Pre-release identifiers (`-alpha.1`, `-rc.2`, etc.) and build metadata (`+sha`)
|
|
31
|
-
|
|
32
|
-
this time. `--force` only accepts bare `MAJOR.MINOR.PATCH` versions.
|
|
27
|
+
- Pre-release identifiers (`-alpha.1`, `-rc.2`, etc.) and build metadata (`+sha`) are valid semver extensions but are
|
|
28
|
+
**not supported by `create-release.sh`** at this time. `--force` only accepts bare `MAJOR.MINOR.PATCH` versions.
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
—
|
|
38
|
-
|
|
30
|
+
Git tags are the only source of truth for versions. Projects keep no version file and make no version-bump commit.
|
|
31
|
+
|
|
32
|
+
**Scope of automated detection:** each project's hook detects only the surface it is written to diff. Breaking changes
|
|
33
|
+
it cannot see — Go type signatures, HTTP API shape, security policy, behavior semantics — are the developer's call: use
|
|
34
|
+
`--force` with the appropriate bump level.
|
|
39
35
|
|
|
40
36
|
---
|
|
41
37
|
|
|
42
38
|
## Core Rule: Tags Are Developer-Created, Never CI-Created
|
|
43
39
|
|
|
44
|
-
CI reacts to pushed tags to run the release pipeline. CI never creates tags.
|
|
45
|
-
|
|
46
|
-
decision requires review and cannot be automated.
|
|
40
|
+
CI reacts to pushed tags to run the release pipeline. CI never creates tags. A version tag is a human commitment about
|
|
41
|
+
what a commit represents — that decision requires review and cannot be automated.
|
|
47
42
|
|
|
48
43
|
---
|
|
49
44
|
|
|
50
45
|
## Tooling
|
|
51
46
|
|
|
52
|
-
| Script | Purpose
|
|
53
|
-
|
|
|
54
|
-
| `scripts/release/create-release.sh`
|
|
55
|
-
| `scripts/release/
|
|
56
|
-
| `
|
|
57
|
-
| `make
|
|
58
|
-
| `make create-release`
|
|
59
|
-
| `make create-release
|
|
47
|
+
| Script or target | Purpose |
|
|
48
|
+
| ------------------------------------------- | --------------------------------------------------------------------- |
|
|
49
|
+
| `scripts/release/create-release.sh` | Canonical (from `keelcore/standards`): compute, approve, tag and push |
|
|
50
|
+
| `scripts/release/create-release.local.sh` | The project's own bump hook; never canonical, never synced |
|
|
51
|
+
| `make create-release` | Alias for `create-release.sh` (auto mode) |
|
|
52
|
+
| `make create-release FORCE=v1.0.0` | Alias for `create-release.sh --force v1.0.0` |
|
|
53
|
+
| `make create-release DRY_RUN=1` | Alias for `create-release.sh --dry-run`: no prompt, no tag, no push |
|
|
54
|
+
| `make create-release-local PREV_TAG=vX.Y.Z` | Run the hook alone against a previous tag |
|
|
60
55
|
|
|
61
56
|
---
|
|
62
57
|
|
|
63
58
|
## How Version Bumps Are Computed
|
|
64
59
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
60
|
+
`create-release.sh` gets the bump level from the hook at `scripts/release/create-release.local.sh` in the git toplevel
|
|
61
|
+
of the repo being released — inside a consumer, the consumer's hook, never the copy in `.standards`. The driver calls it
|
|
62
|
+
as `create-release.local.sh <previous-tag>`, and the hook prints two lines to stdout:
|
|
63
|
+
|
|
64
|
+
1. the bump level: `major`, `minor` or `patch`;
|
|
65
|
+
2. a one-line tag subject (empty means `release vX.Y.Z`).
|
|
66
|
+
|
|
67
|
+
Anything the hook writes to stderr is shown to the developer and recorded in the tag body. A non-zero exit aborts the
|
|
68
|
+
release. With no hook, every release is a `patch`.
|
|
69
|
+
|
|
70
|
+
| Hook reports | Meaning | semver.org rationale |
|
|
71
|
+
| ------------ | ------------------------------------- | -------------------------------------------------------------------------- |
|
|
72
|
+
| `major` | Breaking change to the public surface | Incompatible API change ([item 4](https://semver.org/#spec-item-4)) |
|
|
73
|
+
| `minor` | New backward-compatible functionality | Backward-compatible addition ([item 5](https://semver.org/#spec-item-5)) |
|
|
74
|
+
| `patch` | No public surface change | Bug fixes and internal changes ([item 6](https://semver.org/#spec-item-6)) |
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
While the major version is 0 ([item 9](https://semver.org/#spec-item-9)), `create-release.sh` demotes the bump one step
|
|
77
|
+
(major → minor, minor → patch) and adds `(pre-1.0 demotion)` to the tag subject. A patch stays a patch when there are
|
|
78
|
+
commits since the previous tag; with none, there is nothing to release.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Project Customization
|
|
83
|
+
|
|
84
|
+
A project customizes releases in three places, all owned by the project. Everything else in this policy applies
|
|
85
|
+
unchanged.
|
|
86
|
+
|
|
87
|
+
| What | Where | Purpose |
|
|
88
|
+
| -------------------- | ------------------------------------------------------ | ------------------------------------------------ |
|
|
89
|
+
| Bump policy | `scripts/release/create-release.local.sh` | Decide the bump level from the project's surface |
|
|
90
|
+
| Hook target | `create-release-local` in the project's `Makefile` | Run the hook alone; satisfies `make audit` |
|
|
91
|
+
| Release how-to | The project's `docs/release-policy.md` | Document the project's customization |
|
|
92
|
+
|
|
93
|
+
### The bump hook
|
|
94
|
+
|
|
95
|
+
- It defines the project's public surface for versioning: a config schema, an API spec, exported symbols, a changelog —
|
|
96
|
+
whatever a consumer of the project depends on.
|
|
97
|
+
- It follows the contract in [How Version Bumps Are Computed](#how-version-bumps-are-computed): called with the previous
|
|
98
|
+
tag, two lines on stdout, findings on stderr, non-zero exit to abort.
|
|
99
|
+
- It reports the raw change. It does not apply pre-1.0 demotion or add the demotion note; the driver does both.
|
|
100
|
+
- It is read-only and deterministic: it runs from the git toplevel against the clean `HEAD`, and never tags, commits,
|
|
101
|
+
pushes or writes outside temporary files.
|
|
102
|
+
- It is never canonical. `verify-canonical` and `governance-refresh` skip `*.local.sh`, so `keelcore/standards`' own
|
|
103
|
+
hook is never copied into a project and a project's hook is never overwritten.
|
|
104
|
+
|
|
105
|
+
### The project's release-policy document
|
|
106
|
+
|
|
107
|
+
- It incorporates this policy by reference: it links to `.standards/governance/release-policy.md` and states that the
|
|
108
|
+
standards policy applies in full.
|
|
109
|
+
- It does not restate or contradict this policy. It contains only the project's customization:
|
|
110
|
+
- what the hook diffs, and why that is the project's public surface;
|
|
111
|
+
- the breaking changes the hook cannot see, which call for `--force`;
|
|
112
|
+
- how the hook's input is kept current (e.g. a generated schema and the check that keeps it fresh);
|
|
113
|
+
- project-specific tooling and anything the hook adds to the tag message.
|
|
114
|
+
|
|
115
|
+
### Not customizable
|
|
116
|
+
|
|
117
|
+
- Tags are created only by a developer running `make create-release`; never by CI.
|
|
118
|
+
- `scripts/release/create-release.sh` is canonical and byte-identical in every project. Changes go to
|
|
119
|
+
`keelcore/standards` first.
|
|
120
|
+
- The preconditions, pre-1.0 demotion, `--force` rules and the signed annotated tag.
|
|
121
|
+
- Tags are the only version source; no version file, no version-bump commit.
|
|
122
|
+
|
|
123
|
+
### Examples
|
|
124
|
+
|
|
125
|
+
- **keel** — the hook diffs the config field set of `pkg/config/schema.yaml` at the previous tag against `HEAD`:
|
|
126
|
+
removed fields → major; added fields → minor; no change → patch. See keel's `docs/release-policy.md`.
|
|
127
|
+
- **keelcore/standards** — the hook reads `CHANGELOG.md` `## [Unreleased]`: any `### Removed` entry → major; else any
|
|
128
|
+
`### Added` or `### Changed` entry → minor; else patch. An empty `[Unreleased]` fails.
|
|
74
129
|
|
|
75
130
|
---
|
|
76
131
|
|
|
@@ -79,93 +134,68 @@ at `HEAD`. The schema lists every fully-flattened dotted YAML field path in
|
|
|
79
134
|
```bash
|
|
80
135
|
git checkout main && git pull --ff-only
|
|
81
136
|
# ... merge your work ...
|
|
82
|
-
|
|
137
|
+
make create-release DRY_RUN=1 # optional preview
|
|
138
|
+
make create-release
|
|
83
139
|
```
|
|
84
140
|
|
|
85
141
|
The script will:
|
|
86
142
|
|
|
87
143
|
1. Verify you are on `main`, your branch's configured upstream is in sync, and the working tree is clean.
|
|
88
|
-
2.
|
|
89
|
-
3. Print the summary (
|
|
144
|
+
2. Run the project's hook and compute the next version.
|
|
145
|
+
3. Print the summary (the hook's findings, bump level, proposed tag subject).
|
|
90
146
|
4. Prompt for `y/N` approval.
|
|
91
|
-
5. On approval: create a signed annotated tag and push
|
|
147
|
+
5. On approval: create a signed annotated tag and push that one tag to the upstream remote.
|
|
92
148
|
|
|
93
|
-
|
|
94
|
-
publish).
|
|
149
|
+
A dry run prints the version and the full tag message without prompting, tagging or pushing, and reports failed
|
|
150
|
+
preconditions as warnings. CI picks up the pushed tag and runs the release pipeline (build, sign, SBOM, publish).
|
|
95
151
|
|
|
96
152
|
---
|
|
97
153
|
|
|
98
154
|
## Forcing a Specific Version
|
|
99
155
|
|
|
100
|
-
Use `--force vX.Y.Z` to override the computed version. Only bare
|
|
101
|
-
versions are accepted — pre-release extensions (`-alpha.1`) and build metadata
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
./scripts/release/create-release.sh --force v1.0.0
|
|
106
|
-
```
|
|
156
|
+
Use `--force vX.Y.Z` (`make create-release FORCE=vX.Y.Z`) to override the computed version. Only bare
|
|
157
|
+
`MAJOR.MINOR.PATCH` versions are accepted — pre-release extensions (`-alpha.1`) and build metadata (`+sha`) are not
|
|
158
|
+
supported.
|
|
107
159
|
|
|
108
160
|
### Pre-1.0 rules (current major = 0)
|
|
109
161
|
|
|
110
|
-
Per [semver.org item 9](https://semver.org/#spec-item-9), `0.y.z` is for initial
|
|
111
|
-
|
|
162
|
+
Per [semver.org item 9](https://semver.org/#spec-item-9), `0.y.z` is for initial development where the API is not yet
|
|
163
|
+
stable. Only the following targets are accepted:
|
|
112
164
|
|
|
113
165
|
- Any `0.x.y` where `0.x.y > current` — normal initial-development progression.
|
|
114
|
-
- Exactly `v1.0.0` — the stable API promotion per [semver.org item 10](https://semver.org/#spec-item-10),
|
|
115
|
-
|
|
166
|
+
- Exactly `v1.0.0` — the stable API promotion per [semver.org item 10](https://semver.org/#spec-item-10), regardless of
|
|
167
|
+
the hook's bump level.
|
|
116
168
|
|
|
117
169
|
Forcing `v1.1.0`, `v2.0.0`, or any other version outside this range is rejected.
|
|
118
170
|
|
|
119
171
|
### Post-1.0 rules (current major ≥ 1)
|
|
120
172
|
|
|
121
|
-
Once `1.0.0` is released the public API is stable. `--force` is restricted to
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| `--force` target | Condition | Result |
|
|
125
|
-
| --- | --- | --- |
|
|
126
|
-
| `cur_maj.cur_min.(cur_pat+1)` | any | **Allowed** — single patch step |
|
|
127
|
-
| `cur_maj.(cur_min+1).0` | any | **Allowed** — single minor step (e.g. internal improvements not reflected in schema diff) |
|
|
128
|
-
| `(cur_maj+1).0.0` | breaking changes detected AND matches computed version | **Allowed** |
|
|
129
|
-
| `(cur_maj+1).0.0` | no breaking changes, or doesn't match computed | **Rejected** — major bump requires a detected breaking change |
|
|
130
|
-
| anything else | — | **Rejected** — must be a single-step increment |
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## Keeping schema.yaml Fresh
|
|
135
|
-
|
|
136
|
-
`pkg/config/schema.yaml` is a committed artifact generated from `config.Config`
|
|
137
|
-
by reflection. The pre-commit hook regenerates and stages it automatically
|
|
138
|
-
whenever any `pkg/config/*.go` file is part of a commit — no manual step required.
|
|
139
|
-
|
|
140
|
-
To regenerate it outside of a commit (e.g. to inspect the current output):
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
./scripts/release/gen-schema.sh
|
|
144
|
-
# or
|
|
145
|
-
make gen-schema
|
|
146
|
-
```
|
|
173
|
+
Once `1.0.0` is released the public API is stable. `--force` is restricted to exact single-step increments per
|
|
174
|
+
semver.org's ordering rules:
|
|
147
175
|
|
|
148
|
-
|
|
149
|
-
|
|
176
|
+
| `--force` target | Condition | Result |
|
|
177
|
+
| ----------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
178
|
+
| `cur_maj.cur_min.(cur_pat+1)` | any | **Allowed** — single patch step |
|
|
179
|
+
| `cur_maj.(cur_min+1).0` | any | **Allowed** — single minor step (e.g. changes the hook cannot see) |
|
|
180
|
+
| `(cur_maj+1).0.0` | hook reports `major` AND matches computed version | **Allowed** |
|
|
181
|
+
| `(cur_maj+1).0.0` | hook does not report `major`, or no match | **Rejected** — a major bump requires the hook to report a breaking change |
|
|
182
|
+
| anything else | — | **Rejected** — must be a single-step increment |
|
|
150
183
|
|
|
151
184
|
---
|
|
152
185
|
|
|
153
186
|
## What Goes in the Tag Message
|
|
154
187
|
|
|
155
|
-
The script
|
|
188
|
+
The script generates a signed annotated tag with a structured message:
|
|
156
189
|
|
|
157
|
-
- **Subject line:** `{bump}: {
|
|
158
|
-
- **Body:**
|
|
159
|
-
provenance trailers (`Generated-by:`, `Schema:`)
|
|
190
|
+
- **Subject line:** `{bump}: {hook subject}` (e.g. `minor: added 2 config field(s)`)
|
|
191
|
+
- **Body:** bump level, previous/new version, commit SHA, the hook's findings, and the `Generated-by:` trailer
|
|
160
192
|
|
|
161
|
-
The GitHub Release body
|
|
162
|
-
`gh release create --notes-from-tag`.
|
|
193
|
+
The GitHub Release body can be populated directly from the tag message via `gh release create --notes-from-tag`.
|
|
163
194
|
|
|
164
195
|
---
|
|
165
196
|
|
|
166
197
|
## Emergency / Break-Glass
|
|
167
198
|
|
|
168
|
-
If a critical patch must ship and the normal flow is blocked, follow the
|
|
169
|
-
break-glass
|
|
170
|
-
|
|
171
|
-
requirement, only the precondition checks.
|
|
199
|
+
If a critical patch must ship and the normal flow is blocked, follow the break-glass procedure in
|
|
200
|
+
[`docs/break-glass.md`](../docs/break-glass.md). The bypass still requires two senior engineers and an incident record
|
|
201
|
+
— it does not bypass the tag-creation requirement, only the precondition checks.
|