§ 1.Core Philosophy #
This framework sets our standard for high-velocity R&D through intent-based governance. We use Trunk-Based Development on main to keep integration and learning continuous. Delivery follows the Basecamp Shape Up cadence: 6-week work cycles followed by 2-week cool-downs. Governance is anchored by Signed Tags — the primary, immutable evidence of both technical peer review and functional validation.
§ 2.The Artifact Library #Evidence as Code
The audit trail lives in the repository and in automated pipeline logs, so every release is cryptographically linked to its authorization.
| Artifact | Location | Purpose |
|---|---|---|
| Bet Record | /governance/bets/*.md | The authorized "Pitch" defining appetite, problem, and boundaries. |
| Pipeline logs | — (automated) | The automated, immutable record of Mend.io security scans and deployment steps. |
| Release Ledger | /governance/ledger.md | The history of production releases, capturing commit hashes, authors, approvers, and validation links. |
| Signed Tags | signed-* | Cryptographic evidence of authorization and functional sign-off. Examples: signed-v1.4.0, signed-2025-q1 |
§ 2.1Traceability Contract #
Every production release must satisfy a deterministic evidence chain.
Traditional workflow (new code deployment):
Snapshot workflow (promoting deployed state):
See Appendix A for snapshot-based releases.
Linkage rules
- Every commit message must reference a Bet Record ID (
bet:<slug>). - An
rc-tag points to exactly one commit onmain, but a Bet Record may produce manyrc-tags before one is signed. - Every
signed-tag must reference: therc-tag it promotes (if applicable), the Bet Record ID, and the pipeline run ID. - Every Ledger entry must include: commit hash,
signed-tag, Approver identity, and links to the pipeline log and Bet Record.
Enforcement. CI rejects rc- tags whose commit lacks a Bet Record ID, and signed- tags missing any required reference. Broken chains never reach production — they fail at the gate, not in review.
§ 2.2Revocation #
A signed- tag is immutable, but its trust is not. If post-release analysis invalidates an authorization, the Approver appends a Revocation Entry to the Release Ledger citing the original tag, the reason, and the remediating signed- tag (if any). The original tag remains in history as evidence of what was authorized; the Ledger is the source of truth for what is currently trusted.
§ 3.The Triple-Guardrail Controls #
C1Independent Verification #
Mend.io runs in block mode within the pipeline for every rc- tag. This gate stops vulnerable code from progressing to sign-off.
C2Agent Config Shield #
The .agent-config.json file (which defines model versions and system prompts) is protected by CODEOWNERS and branch protection. Any change requires a review signature from an identity outside the developer group.
C3The Signed Handshake #Four-Eyes Principle
Production promotion requires a signed- tag from an identity other than the requester.
§ 3.1Control Responsibility Matrix #
| Control Area | System (Agent / Pipeline) | Human Responsibility | Override Authority |
|---|---|---|---|
| Code generation | AI Agent proposes commits | Developer validates intent and correctness; commits under their identity | Developer (always) |
| Security validation | Mend.io scan in block mode | Approver confirms results in pipeline log | None — gate cannot be bypassed |
| Functional validation | — | Developer demonstrates behavior; Approver confirms against Bet Record | Approver |
| Release authorization | — | Approver applies signed- tag |
Approver ≠ requester (C3) |
| Agent config integrity | Pipeline enforces CODEOWNERS on .agent-config.json |
Independent signer approves changes | Non-developer identity only |
Principle. Machines validate consistency and policy. Humans validate intent and correctness. The Agent never signs.
§ 4.The "Professional Handshake" SOP #Formally: Release Authorization Protocol
A three-step procedure that converts a commit on main into a release-grade, signed artifact.
A developer tags a commit on main to signal technical readiness. This triggers the automated Mend.io scan.
Phase ASecurity. Approver confirms Mend.io results are "Green" in the pipeline logs.
Phase BFunctional. Developer runs a functional review; Approver confirms the code meets the Bet Record.
The Approver pushes the signed- tag. This serves as a non-repudiable signature confirming the artifact meets all requirements.
§ 4.1Cool-down Protocol #Hotfix Path
Urgent patches bypass the 6-week rhythm but retain the same controls:
- Branch from
main, tagrc-hotfix-v*— triggers C1 (Mend.io scan). - Apply the C3 Signed Handshake — the Approver signs
signed-hotfix-v*. - Record in the Release Ledger with a "Hotfix" justification and a link to the incident or vulnerability that motivated it.
The Appetite is not extended. Hotfix work consumes cool-down capacity.
§ 5.FAQ: Governance & Methodology #
TBD minimizes integration latency by promoting small, frequent commits to main with continuous automated validation. We shift the governance control point from the PR to two stronger anchors: automated pipeline gates (security, build, tests) and cryptographic attestation of authorization via Signed Tags. PRs are a review surface; Signed Tags are an authorization artifact. We prioritize deterministic, auditable promotion over asynchronous review workflows — the review still happens, it just doesn't gate the merge.
Through the Signed Handshake. A signed- tag is a formal approval from a second identity after they have reviewed the code's functional output and the machine's security scans.
See § 4.1 — the Cool-down Protocol. Hotfix work passes the same C1 scan and C3 Signed Handshake as normal releases; it does not extend the Appetite.
Accountability stays with the human team. The C1 Mend.io scan is our automated judge, and the C3 human signature is our final gate. The Agent is a tool; the Signer is the owner.
Via the Player-Coach Protocol. The system rejects any signed- tag where the Tagger is also the Author of the commits. If the CIO writes code, the Product Manager must serve as the Approver.
The Circuit Breaker kicks in: incomplete projects are archived. This prevents "Zombie Projects" and ensures the team only ships what is fully validated.
§ 6.Glossary #
| Term | Definition |
|---|---|
| Appetite | The fixed time budget (6 weeks) we are willing to spend. |
| Shadow Tickets | Automated traceability placeholders mapping commits to business requirements. |
| Circuit Breaker | A hard stop at 6 weeks that preserves the integrity of the team's Appetite. |
| Revocation Entry | A Ledger record that withdraws trust from a previously signed- tag without rewriting history. |
| Snapshot Release | A release created by tagging the current deployed state across all apps, rather than deploying new code. Uses the commit with the highest Docker tag. |
| Sequential Tag | A Docker image tag using incrementing build numbers (e.g., 22, 33, 44) assigned by the CI pipeline for each build. |
| Dual Tagging | The process of applying the same signed-* tag to both the Git repository (commit) and Docker images in the container registry. |
| Discovery Phase | The process of querying deployed apps to determine their current commit hashes and Docker tags before creating a release. |
APPENDIX Snapshot-Based Release Workflow #
A.1Understanding the Monorepo Release Model #
In our monorepo setup:
- Multiple apps (app1, app2, app3) are built from commits on the
mainbranch - Each commit triggers CI pipeline runs that produce Docker images with sequential tags (build numbers: 22, 33, 44, etc.)
- Not every app rebuilds on every commit — only apps with changed code get new images
- The highest Docker tag across all deployed apps indicates the latest commit in the deployment
Key principle: Because this is a monorepo, all apps deployed at or before commit X will have Docker tags ≤ the tag of the app built from commit X.
A.2Discovery Phase: Finding What's Deployed #
Before creating a release, you must discover the current deployment state.
For Product Managers (Non-Technical)
Check the UI footer on each app, or use the discovery script:
node scripts/release-tagger.js --discover
For Developers
Option 1: Query health endpoints (if implemented):
curl https://app1.yourcompany.com/health
curl https://app2.yourcompany.com/health
curl https://app3.yourcompany.com/health
Option 2: Check environment variables on the host
Option 3: Use automated discovery script
Example Discovery Output
Querying apps...
✓ app1: commit 43jdfd342, docker tag 33
✓ app2: commit 3289h3292, docker tag 44 ← HIGHEST
✓ app3: commit v04343432, docker tag 22
Latest commit identified: 3289h3292 (docker tag 44)
The commit with the highest Docker tag is your release candidate.
A.3Creating a Signed Release #
Once you've identified the latest commit, follow these steps:
Step 1: Verify the Commit in Git
# Ensure the commit exists on main branch
git log --oneline | grep 3289h3292
# Verify it's on main
git branch --contains 3289h3292
Step 2: Choose Your Tag Format
The signed-* prefix is required. Choose a suffix based on your release cadence:
| Format | Pattern | Example | Use Case |
|---|---|---|---|
| Semantic | signed-v<M>.<m>.<p> |
signed-v1.4.0 |
Incremental feature releases with version tracking |
| Quarterly | signed-<YYYY>-q<N> |
signed-2025-q1 |
Business milestone releases (quarterly planning) |
| Date-based | signed-<YYYY>-<MM>-<DD> |
signed-2025-05-14 |
Ad-hoc releases (demos, client deliveries) |
| Hotfix | signed-hotfix-v<M>.<m>.<p> |
signed-hotfix-v1.3.2 |
Emergency patches with version increment |
Naming rules:
- Prefix must be
signed-(pipeline trigger contract) - Use hyphens, not underscores:
signed-2025-q1✅,signed_2025_q1❌ - Lowercase only:
signed-v1.4.0✅,SIGNED-V1.4.0❌
Step 3: Tag Git Repository
# Example: Quarterly release
git tag -a signed-2025-q1 3289h3292 -m "Q1 2025 Release | Apps: 22,33,44"
git push origin signed-2025-q1
# Example: Semantic version
git tag -a signed-v1.4.0 3289h3292 -m "Release v1.4.0 | All apps snapshot"
git push origin signed-v1.4.0
Step 4: Tag Docker Images (Dual Tagging)
You must tag all deployed Docker images with the same signed-* tag.
Automated approach (recommended):
# Use the release tagger script
node scripts/release-tagger.js --tag signed-2025-q1 --docker-tags 22,33,44
# Or auto-discover and tag
node scripts/release-tagger.js --tag signed-2025-q1 --auto
The script will:
- Tag all specified Docker images in the container registry
- Create a release manifest JSON in
/releases/signed-2025-q1.json - Update the Release Ledger in
/governance/ledger.md
Step 5: Trigger Release Pipeline
The Azure Release Pipeline should be configured to trigger on signed-* tags. This pipeline includes a manual approval gate (C3 control).
Logical flow:
- Pipeline detects new
signed-*tag in Git - Pipeline reads the tag to identify which Docker images to pull (from registry)
- Manual Approval Gate — Approver reviews release manifest, ledger entry, and changes
- After approval, pipeline pulls Docker images by
signed-*tag - Pipeline deploys to beta or production environment
A.4Visual Workflow Diagram #
┌──────────────────────────────────────────────────────────────────┐
│ SNAPSHOT-BASED RELEASE: From Deployment to Production │
└──────────────────────────────────────────────────────────────────┘
[Apps Running in Alpha Environment]
┌──────────────────────────────────────────────────┐
│ app1: /health → { commit: "43jdfd342", tag: 33 } │
│ app2: /health → { commit: "3289h3292", tag: 44 } │ ← HIGHEST TAG
│ app3: /health → { commit: "v04343432", tag: 22 } │
└──────────────────────────────────────────────────┘
↓
[Discovery: Query Apps]
node scripts/release-tagger.js --discover
↓
Latest commit: 3289h3292 (docker tag 44)
↓
┌─────────────────────────────────┐
│ Dual Tagging Process │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Git: Tag Commit │
│ git tag signed-2025-q1 │
│ → commit 3289h3292 │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Docker: Tag Images │
│ duckbook.azurecr.io │
│ Tag 22,33,44 → signed-2025-q1 │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Release Manifest Created │
│ /releases/signed-2025-q1.json │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Ledger Entry Added │
│ /governance/ledger.md │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Azure Release Pipeline │
│ Triggered by: signed-2025-q1 │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Manual Approval Gate (C3) │
│ Approver: PM or CIO │
│ Reviews manifest & changes │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Deploy to Beta/Production │
│ Pull images: signed-2025-q1 │
└─────────────────────────────────┘
Evidence Chain:
Deployment State → Discovery → Git Tag → Docker Tags
→ Manifest → Ledger → Pipeline → Approval → Production
A.5Integration with Existing Controls #
Snapshot releases comply with the Triple-Guardrail controls:
| Control | How It's Satisfied |
|---|---|
| C1 · Independent Verification | Mend.io scans already passed for all builds during dev pipeline. No new code is deployed, only existing verified images. |
| C2 · Agent Config Shield | No .agent-config.json changes occur in snapshot releases (only tagging existing artifacts). |
| C3 · Signed Handshake | Pipeline manual approval gate enforces the Four-Eyes Principle. Approver must be different identity than the requester who created the signed-* tag. |
A.6Troubleshooting Common Issues #
| Problem | Solution |
|---|---|
| "Can't find commit hash on app" | 1. Check UI footer 2. Query /health endpoint (if implemented)3. Check environment variables on host |
| "Apps show different commit hashes" | This is expected in a monorepo. Not every app rebuilds on every commit. Use the highest Docker tag to identify the latest commit across all apps. |
| "Which commit should I tag?" | Tag the commit associated with the highest Docker tag number. This commit represents the latest state across all deployed apps. |
| "Docker tag command fails" | Run az acr login --name duckbook to authenticate with Azure Container Registry. |
| "Which tag format should I use?" | Use semantic (signed-v1.4.0) for technical version tracking, or date-based (signed-2025-q1) for business milestone releases. |
| "Pipeline doesn't trigger" | Verify the tag uses the signed-* prefix. Check Azure Pipeline trigger configuration. |
A.7Example: Complete Release Flow #
Here's a complete example of creating a quarterly release:
# ────────────────────────────────────────────────────
# STEP 1: Discover current deployment
# ────────────────────────────────────────────────────
$ node scripts/release-tagger.js --discover
Querying apps...
✓ app1: commit 43jdfd342, docker tag 33
✓ app2: commit 3289h3292, docker tag 44 ← HIGHEST
✓ app3: commit v04343432, docker tag 22
Latest commit identified: 3289h3292 (docker tag 44)
# ────────────────────────────────────────────────────
# STEP 2: Verify commit in git
# ────────────────────────────────────────────────────
$ git log --oneline | grep 3289h3292
3289h3292 feat: add streaming data feed bet:market-data
$ git branch --contains 3289h3292
* main
# ────────────────────────────────────────────────────
# STEP 3: Create Git tag
# ────────────────────────────────────────────────────
$ git tag -a signed-2025-q1 3289h3292 -m "Q1 2025 Release | Apps: 22,33,44"
$ git push origin signed-2025-q1
# ────────────────────────────────────────────────────
# STEP 4: Tag Docker images (automated)
# ────────────────────────────────────────────────────
$ node scripts/release-tagger.js --tag signed-2025-q1 --auto
Connecting to duckbook.azurecr.io...
✓ Tagged duckbook.azurecr.io/app1:33 → signed-2025-q1
✓ Tagged duckbook.azurecr.io/app2:44 → signed-2025-q1
✓ Tagged duckbook.azurecr.io/app3:22 → signed-2025-q1
Release manifest saved to: /releases/signed-2025-q1.json
✓ Appended entry to /governance/ledger.md
# ────────────────────────────────────────────────────
# STEP 5: Trigger Release Pipeline & Verify
# ────────────────────────────────────────────────────
# Navigate to Azure DevOps → Pipelines → "Release Pipeline"
# [Manual Approval Gate] → Approver reviews and approves
# After approval → Deploy to Beta/Production
A.8Quick Reference Card #
# ═══════════════════════════════════════════════════
# DISCOVERY
# ═══════════════════════════════════════════════════
# Find what's deployed
node scripts/release-tagger.js --discover
# Check health endpoint (if implemented)
curl https://app1.company.com/health
# ═══════════════════════════════════════════════════
# GIT TAGGING
# ═══════════════════════════════════════════════════
# Tag the commit with highest Docker tag
git tag -a signed-<NAME> <COMMIT_HASH> -m "<DESCRIPTION>"
git push origin signed-<NAME>
# Examples:
git tag -a signed-v1.4.0 abc123 -m "Release v1.4.0"
git tag -a signed-2025-q1 abc123 -m "Q1 2025 Release"
# ═══════════════════════════════════════════════════
# DOCKER TAGGING
# ═══════════════════════════════════════════════════
# Automated (recommended)
node scripts/release-tagger.js --tag signed-<NAME> --auto
# Manual
az acr login --name duckbook
docker tag duckbook.azurecr.io/<app>:<BUILD> duckbook.azurecr.io/<app>:signed-<NAME>
docker push duckbook.azurecr.io/<app>:signed-<NAME>
# ═══════════════════════════════════════════════════
# VERIFICATION
# ═══════════════════════════════════════════════════
# List signed tags
git tag -l "signed-*" | sort -V
# View changes in release
git log <PREV_SIGNED>..<NEW_SIGNED> --oneline
# Check manifest
cat /releases/signed-<NAME>.json
# Check ledger
tail /governance/ledger.md