Release Manifest — design reference
The long-form companion to SDD — Version Control Service. The SDD carries the decisions that need approval; this page carries the evidence behind them — what the code does today, what the snapshot looks like, how the checklist is generated, and what fits into the delivery window.
Sources: release-manifest-guide7.docx (reference and developer guide v1.0),
release-manifest-guide-addendum_v9.docx (Docker Pull column),
release-manifest-v11.html (working prototype, all data mocked).
Requirements
Traceable identifiers used throughout. Source: guide (G) and addendum (A).
| ID | Requirement | Source |
|---|---|---|
R1 |
Releases are grouped in the sidebar under their Solution family; hot-fixes render above sprints within a group |
G 1.3 |
R2 |
The Solution version is derived from the FTACS major.minor version shipped in that release |
G 1.4, 2.2 |
R3 |
Release Notes list the Jira items shipped, split into Tasks/Features and Bug fixes, each with key, link and the verbatim summary |
G 1.3 |
R4 |
Bug items show the affected customer name |
G 1.3, 2.3 |
R5 |
Hot-fix releases show only the Bug fixes column and no version table |
G 1.3 |
R6 |
The version table lists every component grouped by deployment layer, with version, build, status badge and changelog link |
G 1.3 |
R7 |
The status badge is one of updated, stable, breaking, hotfix, new — computed against the previous release |
G 1.3, 2.3 |
R8 |
Compatibility renders an ACS pivot block plus per-component Requires and Used-by cards, from a single directed dependency list |
G 1.3, 2.3 |
R9 |
On a hot-fix, Compatibility narrows to the patched components plus their direct dependencies |
G 1.3 |
R10 |
The deployment checklist has one expandable entry per component needing action, with change-type tags, a plain-English summary, template links and a git-style diff with per-file tabs |
G 1.3, 2.4 |
R11 |
Breaking entries carry the red border and the warning banner |
G 1.3, 2.4 |
R12 |
Upgrade Path: From/To dropdowns, chronological walk, later entry supersedes earlier, aggregated release notes, path badges, validation of same-version and downgrade input |
G 1.5, 2.8 |
R13 |
Page state is in-memory only; the published page is read-only |
G 2.9 |
A1 |
The version table has a Docker Pull column with click-to-copy per FT component; third-party rows show a dash; the column is absent on hot-fixes |
A 1.3a |
A2 |
The Harbor path per component is data, not derived from the display name |
A 2.3a |
Non-functional: the published artifact is self-contained with no build step and readable offline (N1); a published manifest is immutable history (N2); generation must never block or fail the finalize flow (N3); all three test layers are exercised (N4).
Gap analysis
Verified against the working tree.
Already available
|
Against The single row marked (missing) is the one capability |
| Need | Where it lives now |
|---|---|
Release record: sprint name, URL, author, per-project version, build, status, steps |
|
Harbor image URL, tag and digest per project |
Project result fields |
Component catalogue for FT services |
|
Jira tickets per release, with issue type and a customer flag |
|
Docs-portal commit and build trigger |
|
Finalize e-mail with a call-to-action button |
|
Hot-fix builds with branch, version and build tag |
|
Third-party images with pinned tags, per-service variable contracts, config lists, dependency edges |
The |
JSON storage with a locked read-modify-write, and a retention pattern to copy |
|
Reading arbitrary repository files at a given ref (missing) |
Needs a REST transport in |
Missing — the actual work
| # | Gap | Consequence |
|---|---|---|
G1 |
No Solution version concept anywhere. Deployment Manager deliberately dropped |
R1, R2 and R12 have no data source. The concept is introduced here and must not be pushed back into Deployment Manager. |
G2 |
No layer attribute on components. |
R6 |
G3 |
The databases are absent from |
R6, R8 |
G4 |
Jira tickets are computed during changelog generation and never persisted. |
R3 — historic manifests could not be rebuilt without re-walking commits and re-querying Jira. |
G5 |
|
R4 |
G6 |
No previous-state snapshot, so updated versus stable versus new cannot be computed. |
R7 |
G7 |
Nothing captures deployment actions: summary text, breaking flag, template links, diffs. |
R10, R11 |
G8 |
|
R5 — hot-fix manifests must be snapshotted at build time, not derived later. |
G9 |
The prototype is one 1646-line file mixing data, rendering and styling. |
N1, plus the repository’s file-size rule. |
Specification deviations
Deviation 1 — Harbor tag format. The addendum specifies <version>_<buildNum>, e.g.
hub.friendly-tech.com/ui/portals:7.0.6_0.0.2. The tag the pipeline actually pushes is
v{version}-b{build}; a real record carries
hub.friendly-tech.com/configs/ft-configs-service:v1.0.1-b0.0.1. A support engineer
copying the addendum-formatted command would get a not-found from the registry.
Resolution: the Docker Pull column renders the recorded imageUrl verbatim; no tag is
recomputed. If the Harbor convention does change, the manifest follows it for free.
Deviation 2 — customer name. The guide shows a customer on bug items. The client only
knows whether a ticket has a linked asset (hasCustomer from customfield_10183, whose
value is a list of Assets objects). Whether the response carries a readable label, or a
second Assets call is required, is unverified. A time-boxed spike decides; the fallback is
a manual field.
Design decisions
| ID | Decision |
|---|---|
D1 |
A module in CI/CD Manager, published as a static artifact. All inputs are local; the docs-portal publication path already exists. Rejected: a standalone application (would fetch all of its data back over HTTP), and a module in Deployment Manager (owns contracts, not releases). Exit path: because the artifact reads one JSON document, extracting a renderer service later is a file move. |
D2 |
A manifest is an immutable, self-sufficient snapshot. It embeds the catalogue, edges, versions, items and checklist as they were, and never joins to live data at read time. Cost: duplication of roughly 30-60 KB per release. Accepted. |
D3 |
Solution version is derived, not entered. |
D4 |
The catalogue is composed, not re-entered. FT components from |
D5 |
Dependency edges are unified, with |
D6 |
Status is computed by diffing against the previous published snapshot. new = absent before; updated = version or build changed; stable = unchanged; hotfix = build-only change on a hot-fix; breaking = updated and the checklist entry is marked breaking. |
D7 |
Generated draft plus human override, and the override wins. Every field records whether it is generated or edited; regeneration never overwrites an edited field. A tool that silently discards a human correction is worse than no tool. |
D8 |
Contract diffs are computed between snapshots, not git refs. Each manifest stores a contract fingerprint. No dependency on the contracts repository being tagged, and history stays correct even if that repository is rewritten. It also side-steps Deployment Manager keeping only the latest contract row. |
D9 |
One renderer, two consumers. A pure function from bundle to DOM, fed from the API in the app and inlined in the export. Upgrade Path stays client-side, as prototyped. |
D10 |
Generation is a side effect of finalize, never a gate. The draft is built after the docs-portal phase and before the e-mail; failure logs a warning and the release continues. |
D11 |
Manifests are stored as JSON on |
Snapshot document
Shaped as the union of the prototype’s top-level constants, so the renderer consumes it with no adaptation layer.
{
"schemaVersion": 1,
"id": "sprint-7",
"type": "sprint",
"solution": "sol-7",
"solutionVersion": "7.0",
"name": "Sprint 7",
"period": "June 2025",
"date": "2025-06-18",
"notes": "<html allowed>",
"updatedCount": 7,
"components": [
{ "id": "ftacs", "layer": "core", "name": "FTACS (ACS Server)",
"sub": "TR-069 / USP / MQTT", "slug": "ft-acs",
"image": "ftacs/ftacs", "core": true }
],
"layers": [ { "id": "db", "label": "Layer 1 - Databases" } ],
"solutions": [ { "id": "sol-7", "label": "Solution 7.x", "color": "#388BFD" } ],
"deps": [ ["ftacs", "mysql"] ],
"items": [
{ "type": "bug", "jira": "DEV-2097", "jiraUrl": "https://.../browse/DEV-2097",
"title": "OneIoT ACS > Provision > ...", "customer": "Proximus BE",
"component": "Provision API", "status": "fixed" }
],
"versions": {
"ftacs": { "version": "7.0.0", "build": "b.0.0.1", "status": "updated",
"pull": "hub.friendly-tech.com/ftacs/ftacs:v7.0.0-b0.0.1",
"changelogUrl": "https://docs.friendly-tech.com/services/ft-acs/changelog" }
},
"checklist": [
{ "compId": "ftacs", "tags": ["env", "compose", "dbmigr"], "breaking": true,
"summary": [ { "icon": "<emoji>", "text": "...",
"fileLink": { "label": "...", "href": "..." },
"origin": "generated" } ],
"diffs": [ { "file": ".env", "origin": "generated",
"hunks": [ { "type": "removed", "lineOld": 14, "text": "..." } ] } ],
"origin": "edited" }
],
"contracts": {
"ftacs": { "image": "hub.friendly-tech.com/ftacs/ftacs", "defaultTag": null,
"variables": { "ACS_WEB_SERVICE_PORT": { "default": "8080", "required": false } },
"configs": ["config.d/..."], "ports": [8080],
"templateSha": "9f2c..." }
},
"source": { "releaseId": "4390807f-...", "generatedAt": "2026-07-30T09:00:00Z",
"generatedBy": "...@friendly-tech.com", "generator": "1.0" }
}
pull is stored resolved — it is the release record’s imageUrl, never a formula.
contracts is the fingerprint the next release diffs against and is not rendered.
Checklist generation
Input: this release’s contract fingerprint against the previous published manifest’s.
| Detected | Tag | Generated content |
|---|---|---|
Variable added, removed, or default changed |
|
A summary line naming the variable and a |
Image or default tag changed |
|
A summary line with old and new tag, plus a compose hunk |
Entry added to the config list |
|
A summary line naming the file and its target path |
Ports or volumes changed |
|
A summary line plus a hunk |
Compose template changed with no semantic delta |
|
A line flagging "template changed, review manually" |
Nothing changed and the version is unchanged |
|
A quiet row |
dbmigr and breaking are never inferred — a schema change is not visible in a contract.
Both are set by the release manager, and breaking is what promotes the version-table badge.
Generation and publication flow
Sprint. The finalize orchestration runs phases 0 through 7. Two insertions: the resolved Jira items are persisted during the changelog phase, and a draft is requested after the docs-portal phase and before the e-mail. Draft assembly resolves and freezes the catalogue, derives the Solution version, fills versions (released projects from the release record, everything else inherited from the previous snapshot, third-party from the contracts), splits the Jira items by issue type, generates the checklist and writes the fingerprint.
Hot-fix. The draft is created at build time, because the build history prunes after 10 days. Only the patched components appear, items are bugs only, no version table, and Compatibility narrows to the patched components plus their direct dependencies.
Publication. The reader is a support engineer at a customer site, so the artifact is published to the documentation portal rather than served from an authenticated admin tool; the authenticated UI exists for the release manager who curates the draft. The finalize e-mail carries a deep link, and it appears only when the manifest actually reached published state.
| Surface | Audience | Content |
|---|---|---|
Version Control tab |
Release manager |
Drafts, checklist editor, preview, publish |
The published page on the portal |
Support engineers |
Every published manifest, both views, no login, offline-capable |
Finalize e-mail |
The announcement list |
A deep link to the release |
Consequence to accept: the published copy is a point-in-time export, so editing a published manifest means re-publishing. That is correct for a document whose value is being an authoritative record.
Delivery schedule
The full design is roughly 19 working days; the window from 31 July to 12 August is 9. Every phase ships at reduced depth. Tests are not part of what gets compressed.
| Day | Date | Deliverable |
|---|---|---|
1 |
Fri 31 Jul |
Manifest storage with atomic writes; catalogue composition; dependency unification and alias map |
2 |
Mon 3 Aug |
Jira Assets spike; persist the resolved Jira items, split by issue type |
3 |
Tue 4 Aug |
Builder: versions, status, solution derivation, contract fingerprint |
4 |
Wed 5 Aug |
Contract reader and diff; generated checklist draft; the finalize hook |
5 |
Thu 6 Aug |
Renderer port: sidebar, release notes, version table, compatibility |
6 |
Fri 7 Aug |
Renderer port: checklist and diff viewer |
7 |
Mon 10 Aug |
Upgrade Path; checklist editor with edited-wins precedence |
8 |
Tue 11 Aug |
Hot-fix manifests; static export and portal publication |
9 |
Wed 12 Aug |
E-mail link; full three-layer suite, fixes, demo |
The original plan had ten days and a dedicated stabilisation day. One day is already gone, so days 1 and 2 absorb what were three days of work, and the backfill has moved out of the window — it was the designated first casualty.
Reduced depth. The generated diff covers variables, image tags and config additions, not ports, volumes or template hashes. The layer attribute ships as a static map rather than a settings field. Backfilled releases get no checklist, because there is no earlier fingerprint to diff against. Hot-fix Compatibility may ship as the full graph with patched components highlighted. The customer-name spike is half a day; publication has no in-app preview mode.
What may not make the date, in the order it would go: Upgrade Path (a standalone view), customer names (already time-boxed), hot-fix manifests (the one with a decay cost). Backfill is already out. Generation at finalize and publication cannot slip.
External dependencies. A layer assigned to each of the 19 projects and the third-party list confirmed by the end of day 1; agreement on who writes the breaking-change prose at finalize. No dependency on the Postgres work — see D11.
Risks
| # | Risk | Mitigation |
|---|---|---|
1 |
Customer identity may need a second Assets call |
Time-boxed spike; manual fallback; the existing flag keeps working |
2 |
Contracts move from the deployments repository into service repositories |
The contract reader reuses the established resolution order, so the move is configuration |
3 |
Mock contracts may not match production files, so a generated diff can be wrong |
Exactly why the human override is authoritative and every generated field is labelled |
4 |
The Harbor tag convention may change |
The manifest echoes the recorded image URL, so it follows automatically |
5 |
The snapshot schema will evolve |
|
6 |
The finalize script is large and carries an existing diff |
One inserted call; the Playwright quality mark goes green first |
7 |
Nine working days against roughly 19 days of design, with no buffer |
Reduced depth per phase; a named drop order; generation and publication are protected |
8 |
The constraints that keep the Postgres move additive erode under deadline pressure |
They are testable, not aspirational: |
9 |
An interrupted write truncates the whole history, because the storage library rewrites in place and reads swallow parse errors |
Manifest storage writes to a temporary file and |
Open questions
-
Q1 — does the Jira Assets field carry a label, or is a second call required? Decided by the day-2 spike.
-
Q2 — who owns the breaking-change prose at finalize: the release manager, or the service team that made the change? Determines whether the editor needs per-component assignment.
-
Q3 — how far back should the backfill go? Upgrade Path is only useful across the Solutions customers actually run.
-
Q4 — do the per-release template guide pages referenced by the guide exist, or must publication generate them? Currently assumed: links are entered by hand, and unresolvable links are omitted.