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 over RELEASES (not solution versions — FT numbers releases inside a minor, so 7.1.1 and 7.1.2 must be two points), chronological walk, later entry supersedes earlier, aggregated release notes, path badges, validation of same-release 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 Re-verified on 31 July 2026 against the working tree and against the live Jira, Harbor and GitHub the application is configured for. Nothing in this table is missing: the row that previously read "needs a REST transport" was wrong, and the source of the deployment facts has moved from the contracts repository to the documentation portal — see D4 and D12. |
| 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 variables, config lists, dependency edges |
The deployment guides in the |
JSON storage with a locked read-modify-write, and a retention pattern to copy |
|
Reading arbitrary repository files at a given ref |
|
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).
Measured on 31 July 2026 against the live instance, so no spike is needed. The issue
payload carries no label:
"customfield_10183": [ { "workspaceId": "bdaadb3f-...", "id": "bdaadb3f-...:49", "objectId": "49" } ]
A second call resolves it, with the same credentials but a different host:
GET https://api.atlassian.com/jsm/assets/workspace/{workspaceId}/v1/object/{objectId}
-> { "label": "RIL - Central", "objectType": { "name": "Projects" } }
DEV bugs carry the field, so this applies to sprint releases and not only to PROD tickets —
DEV-2765 resolves to Bharti Airtel Limited, DEV-2744 to SCG England (via Provu).
Resolution: ft-jira-client gains a customers list resolved through that call, cached by
objectId; a release touches a handful of distinct ids. The label is rendered verbatim — it
is a "Projects" object and may carry a person’s name or a supply chain, and cleaning it up in
code would be guessing. Corrections belong in Jira.
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 |
Guide diffs are computed between snapshots, not git refs. Each manifest stores the guide commit sha and the parsed fingerprint. The portal is unversioned ( |
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. |
D12 |
The deployment facts come from the documentation portal, not from the deployments repository. |
D13 |
No backfill, one frozen baseline instead. The first manifest is for 7.1.2 and needs a 7.1.1 predecessor that never existed. A |
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" }
],
"guide": {
"repo": "Friendly-Technologies/docs-portal",
"sha": "9f2c...", // the commit the guide was read at
"readAt": "2026-07-30T09:00:00Z",
"commitsSincePrevious": [ { "sha": "...", "date": "...", "author": "...", "subject": "..." } ],
"services": {
"ftacs": { "image": "hub.friendly-tech.com/ftacs/ftacs", "tag": "latest",
"variables": { "ACS_WEB_SERVICE_PORT": { "default": "8080" } },
"configs": ["config.d/..."], "ports": [8080],
"anchor": "env-ftacs" }
}
},
"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. build
is stored as the release recorded it (0.0.4); the prototype’s b. prefix is a display
form the renderer applies, and freezing it into the document would be a rendering decision
written into history. source also carries what the release manager has to look at:
unversioned (components with no version from any source — every third-party entry until
the guide is read), unmatched (a released project the catalogue does not know) and
unresolved (a project key the alias map could not place).
guide is the fingerprint the next release diffs against and is not rendered; anchor is
the existing [[env-<service>]] id in full-system-env-reference.adoc, which is what the
checklist links to. No per-release template pages are generated — they already exist.
Checklist generation
Input: this release’s guide fingerprint against the previous manifest’s — parsed structures, never the prose.
Three pages are read at one commit: the compose block in full-system-deployment.adoc (the
primary one only — the Oracle variant is a second block and merging both would describe a
deployment nobody runs), the === <service>/.env sections of
full-system-env-reference.adoc, and the per-service config table in
partials/per-service-config-table.adoc. The table is keyed by the config directory rather
than by its display column, so it introduces no vocabulary of its own — but the guide does
speak two dialects: the compose block names the container (ft-device-network-service) and
the environment reference names the directory (ft-device-network). Both are in the alias
map. The shared Root .env keeps its own entry: it belongs to no component and it is where
a breaking deployment change usually lands. icon is a KEY (env, image, file,
service, none), not a glyph — a snapshot is history, and a glyph frozen into it is a
rendering decision nobody can revisit.
| 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 the guide.
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 guide), 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 |
Customer names through the Assets API; persist the resolved Jira items, split by issue type |
3 |
Tue 4 Aug |
Builder: versions, status, solution derivation, guide fingerprint; the frozen 7.1.1 baseline from Harbor |
4 |
Wed 5 Aug |
Guide 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 |
Renderer port finished; checklist editor with edited-wins precedence; Upgrade Path if the port leaves room |
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. The grilling on 31 July returned about
a day — the Jira spike collapsed into four minutes of measurement, the ft-github-client
change turned out to be unnecessary, and the layer assignment was closed from the prototype
rather than waited on — and that day went to the renderer port, which is the one estimate the
code contradicted. Backfill is not deferred but cancelled; see D13.
12 August is the delivery date, not the operational one. The first real manifest is produced when 7.1.2 is finalized, expected end of August or early September. What must exist by that finalize, not by the 12th: storage with atomic writes, draft generation, and the frozen 7.1.1 baseline. A release state that has passed cannot be reassembled.
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. Hot-fix Compatibility may ship as the full graph with patched components
highlighted. Publication has no in-app preview mode. The AI assist for the breaking-change
prose is a clipboard button, not an integration: settings.json carries no LLM provider, and
this is the one field where a confident hallucination is executed against a customer’s
production.
What may not make the date, in the order it would go: Upgrade Path (a standalone view), hot-fix manifests (the one with a decay cost), then the generated part of the checklist — the manual editor stays either way. Customer names have left this list; they now cost about twenty lines. Generation at finalize, publication and atomic writes cannot slip.
External dependencies — all closed on 31 July, before day 1. Layers taken from the
prototype with Hazelcast moved to layer 1; the third-party list is the seven entries the
prototype carries and projects.json does not; emulators and Superset excluded; the
breaking-change prose is written by the release manager. No dependency on the Postgres work
— see D11 — and none on ft-deployments — see D12.
Risks
| # | Risk | Mitigation |
|---|---|---|
1 |
Customer identity needs a second Assets call |
Closed: measured, implemented, cached by |
2 |
The deployment guides stop being maintained once the manifest exists, and the generator silently produces empty checklists |
The guide sha and its commit date are stored; an unchanged guide is reported as |
3 |
A generated row may be a documentation correction or belong to an earlier release |
Structure is diffed rather than prose, the causing commits are stored beside the rows, and the human override is authoritative — every generated field is labelled |
4 |
The Harbor tag convention may change |
The manifest echoes the recorded image URL, so it follows automatically. Confirmed in the registry: two formats coexist ( |
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 — the |
10 |
The baseline picks a branch build as a version — the newest artifact in most repositories is |
Only tags matching the release pattern are considered, ordered by version rather than push time; a project with no release tag is surfaced for manual entry instead of being skipped, and the whole baseline stays editable |
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 |
Answered questions
Closed during the design review on 31 July 2026, before the first line of code.
-
Q1 — does the Jira Assets field carry a label? No. It carries
objectId, and a second call toapi.atlassian.com/jsm/assets/…resolves the label with the same credentials. Measured, not estimated; see deviation 2. -
Q2 — who owns the breaking-change prose? The release manager, as the single author, with an AI assist through the clipboard. Per-component assignment to service teams is out: it needs a "team was asked, team answered" loop that does not exist yet, and it would leave the manifest unpublishable at finalize time. Every entry records
editedBy, so the question can be reopened later on evidence. -
Q3 — how far back should the backfill go? Nowhere; there is no backfill. One frozen
baselinerecord for 7.1.1 instead — see D13. -
Q4 — do the per-release template pages exist? The question is void:
full-system-env-reference.adocalready carries stable anchors ([[env-ftacs]],[[env-northbound-api]], …[[compose-file]]), so the checklist links to those. Nothing is generated. Unresolvable links are still omitted.
Still open, and neither blocks day 1: the exact date of the 7.1.2 finalize (end of August or early September), and who physically fills in the first checklist.