Skip to content

Introduce immutable job IDs across all job-dependent data and workflows #447

Description

@borg-codex-bot

Summary

Jobs currently have no immutable identity. Their effective identity is derived from mutable and overloaded values, primarily:

job_key = <type_id>_<location>
archive_prefix = <type_id>-backup

The wizard exposes Type ID even though it is not a real job type. The same user input is used to derive the job key, archive prefix, filenames, cache paths, log correlation, status grouping, and joins across dependent stores. The system also appends -backup without making the complete stored archive prefix an explicit user-owned value.

Changing Type ID or location therefore looks like deleting one job and creating another to parts of the application. This causes stale or duplicated dashboard entries and can disconnect historical status, reports, restore tests, schedules, notifications, widgets, repository assignments, and restore history from the logical job the user edited.

Introduce a generated immutable job_id, remove Type ID from the new user-facing and canonical job model, and make the complete archive prefix explicit. Use job_id as the only canonical foreign key across every job-dependent subsystem.

This is an application-wide architecture migration, not a dashboard-only fix.

Current problem

Today different areas infer job identity or behavior from one or more mutable values:

  • job metadata filename and job_key,
  • backup_type / Type ID,
  • location,
  • status and log filenames,
  • archive prefixes derived by appending -backup,
  • repository used_by / source_job_keys,
  • schedule map keys,
  • restore-test filenames,
  • runtime control paths and locks.

For example, changing paperless_storagebox to documents_storagebox removes the old metadata key but keeps historical paperless_storagebox status files. The dashboard currently starts with all status keys and only adds missing configured jobs, so both identities may be displayed even though only one configured job exists.

The UI model is also unclear:

Job name: user-facing display label
Type ID: technical fragment with several hidden consequences
Location: repository/storage property and part of the inferred identity

A user entering paperless does not directly own or see every value derived from it.

Goals

  • Give every job a generated UUID that never changes during the lifetime of that job.
  • Use job_id as the only canonical foreign key between job-dependent stores and runtime services.
  • Replace the user-facing Type ID with an explicit complete archive prefix.
  • Do not append hidden text to the archive prefix entered or accepted by the user.
  • Keep the job name, archive prefix, repository assignment, icon, description, sources, and operational settings editable.
  • Derive location from the selected repository and its storage target instead of using it as identity.
  • Preserve the existing archive_prefixes behavior: the current prefix is first and previous prefixes remain available for restore and retention scoping.
  • Preserve historical descriptor values as run-time snapshots without using them as identity.
  • Keep exactly one dashboard/widget entry per configured job.
  • Preserve reports, history, restore tests, restore runs, and archive access across job name and archive-prefix changes within the current repository.
  • Migrate existing installations once, atomically, idempotently, and with a complete audit trail.
  • Preserve ambiguous legacy data without guessing or silently linking it to the wrong job.

Non-goals

  • Do not replace readable Borg archive prefixes with UUIDs.
  • Do not use UUIDs as authentication tokens or secrets.
  • Do not delete old status, report, restore-test, restore-history, or archive data merely because it cannot be linked automatically.
  • Do not keep two permanent internal identity systems after migration.
  • Do not add repository assignment history.
  • Do not search multiple current or historical repositories for one job.
  • Do not move or copy archives when a repository assignment changes.
  • Do not change repository deletion rules as part of this issue.
  • Do not silently merge jobs because their names, prefixes, repositories, or legacy keys match.

The user-safety warning and confirmation for changing a job repository are tracked separately in #452.

Canonical job model

Example target metadata:

{
  "schema_version": 4,
  "job_id": "6cd32490-99e2-45df-bafb-81b942decd82",
  "name": "Paperless Backup",
  "repository_key": "repo_paperless",
  "archive_prefixes": [
    "documents-backup",
    "paperless-backup"
  ],
  "legacy_job_keys": [
    "paperless_storagebox"
  ]
}

Identity and metadata rules:

  • job_id is generated once with UUIDv4 and is immutable.
  • The metadata filename is <job_id>.json.
  • New jobs do not use job_key, backup_type, Type ID, or location as identity or canonical active metadata.
  • name is the editable display label.
  • repository_key is the one current repository assignment.
  • archive_prefixes is an ordered, de-duplicated list.
  • archive_prefixes[0] is the complete current prefix used for new archives.
  • Remaining archive_prefixes entries are previous complete prefixes used to find and manage existing archives in the current repository.
  • The wizard displays and edits the complete current prefix exactly as stored. It does not append -backup or another hidden suffix.
  • A new job may receive an editable archive-prefix suggestion derived from its display name, but the preview must show the exact resulting archive pattern before save.
  • When the current prefix changes, the previous prefix is retained in archive_prefixes.
  • legacy_job_keys exists only for migration, legacy-data resolution, and a bounded API compatibility period. It is never a new active foreign key.
  • Duplicating or importing a job as a new job always creates a new job_id.
  • Updating an existing job retains its job_id.
  • Location is resolved from the current repository and storage context. Historical records may retain the location used for that run as a snapshot.

Wizard model

The primary identity and naming fields become:

Job name:       Paperless Backup
Archive prefix: paperless-backup
Repository:     Paperless repository
Job ID:         internal and non-editable

The wizard must show an exact preview such as:

paperless-backup-2026-09-02_03-00-00

Required behavior:

  • Do not show Type ID for new or migrated jobs.
  • Do not derive an active key from archive prefix plus location.
  • Validate the complete archive prefix as a safe Borg archive-name prefix.
  • Keep the current prefix first in archive_prefixes.
  • Explain that changing the prefix affects new archives while previous prefixes remain available in the current repository.
  • Preserve all existing job-specific settings when only the name or prefix changes.
  • Handle repository-change confirmation through Warn and require confirmation before changing a job repository #452.

Run status model

New status records include immutable identity and descriptive run snapshots:

{
  "job_id": "6cd32490-99e2-45df-bafb-81b942decd82",
  "job_name": "Paperless Backup",
  "archive_prefix": "paperless-backup",
  "repository_key": "repo_paperless",
  "location": "storagebox",
  "archive_name": "paperless-backup-2026-09-02_03-00-00",
  "status": "success"
}

Snapshot fields do not need an _at_run suffix because a run record is inherently historical. Legacy records may retain backup_type and job_key, but new records and readers must not use them as canonical identity.

Status and log filenames remain readable for administrators. Use the archive prefix or a bounded display slug captured for the run plus a short UUID suffix, for example:

2026-09-02_03-00-00_paperless-backup--6cd32490.status
Borg-Backup_paperless-backup--6cd32490--2026-09-02_03-00-00.log

The complete UUID remains in the JSON payload, runtime state, and structured log context. The short suffix is only a human-friendly correlation aid and is not used as identity. Readers use payload or persisted run metadata as the source of truth and never infer canonical identity from a readable filename.

Repository assignment boundary

A job has exactly one current repository_key.

When the repository assignment changes:

  • job_id remains unchanged.
  • The old current assignment is removed atomically.
  • The new current assignment is added atomically.
  • Future backups use only the new repository.
  • Existing archives are not moved or copied.
  • Browse & Restore for the job uses only the current repository.
  • Historical run records may display the repository used at that time as a snapshot.
  • No repository_history is added to the job.

The dedicated warning and explicit confirmation are implemented separately under #452.

Dependency inventory and required target behavior

Area Current dependency Required behavior with job_id
Job metadata and discovery Metadata filename, job_key, backup_type, and location Store and discover by immutable job_id; expose name, archive prefixes, and current repository separately
Wizard create/edit Type ID and location derive identity and archive prefix Create UUID once; edits retain it; edit the complete current archive prefix explicitly
Jobs API and UI actions Run, cancel, enable, edit, delete, and live log use job_key Address actions by job_id; return readable name and prefix separately
Dashboard Starts from latest .status per mutable key and supplements configured jobs Start from configured jobs by job_id; overlay latest run, runtime state, schedule, restore proof, and repository details
Unraid dashboard widget Joins job/status/cache data by current key Cache and aggregate by job_id; one widget row/count per configured job
Homepage widget API Reads metadata/status/schedules by job_key Use job_id internally and return readable descriptors for display
Backup status Filename and payload use Type ID plus location Write job_id; keep name, archive prefix, repository, and location as run snapshots
Weekly repository snapshots Maps sizes by mutable status key Key snapshots by job_id; preserve historical descriptor metadata where useful
History Parses Type ID/location from status filenames Read status payload and group/filter by job_id; support unassigned legacy records
Reports Discovers report jobs and time series from filenames and parsed job_key Select and aggregate by job_id; name/prefix changes remain one continuous report
Report mail Matches planned jobs, metadata, schedules, and statuses by key Join by job_id; intentionally display current job labels and historical run snapshots
Browse and Restore Uses job_key to resolve repository and archive-prefix filters Select by job_id; resolve the current repository and ordered archive_prefixes
Archive naming and retention Prefix is derived from Type ID Use the explicit current archive_prefixes[0]; keep readable historical prefixes for the current repository
Restore precheck and execution Request and persisted run contain job_key Store job_id plus run-time job, repository, prefix, and archive snapshots
Restore history Summary/detail/index store job_key Store and filter by job_id; keep historical display descriptors
Restore tests <job_key>.test, policies, and plan rows use key Use <job_id>.test; result payload, policy, scheduling, reminders, and proof map use job_id
Schedules and cron schedules.json is keyed by job_key; cron posts job_key Key schedules by job_id; cron posts job_id; readable descriptors remain diagnostic only
Repository assignment Repository used_by and source_job_keys contain mutable keys Replace with job_ids / source_job_ids; derive display labels from current job inventory
Repository reconciliation Compares repository links to metadata job_key Reconcile immutable IDs and report missing IDs explicitly
Repository maintenance Job lookup and archive filtering use mutable key or derived prefix Resolve by ID, then use current repository and explicit prefix list
Runtime manager In-memory state and subprocess environment are keyed by job_key Key runtime state by job_id; include readable values only in logs and snapshots
Cancellation control Runtime paths, control state, and validation use key Stable control ownership by job_id and independent run_id
Resource locks Lock owner and conflict messages use job_key Lock owner is job_id; messages resolve current display labels
Runtime recovery Recovery state stores Type ID/location Store job_id and run snapshots so recovery remains stable after edits
Log files and live log Filename and lookup use mutable job key/type Use stable ID for lookup; keep readable prefix/name in filenames and content
Notifications Events contain job_key, Type ID, and location Correlate by job_id; retain descriptive run snapshots
Reminder state Deduplication keys contain job_key Use job_id to prevent duplicate or lost reminders after edits
Overdue detection Joins schedules, jobs, and latest status by key Join all three by job_id
Source manifests Proposed manifests would otherwise be job-keyed Name and associate manifests by job_id
Import/export Collision handling rewrites job_key; selected jobs use keys Define preserve, merge, duplicate, and collision rules for IDs
Configuration backup/restore Job files and dependent stores use mutable filenames/keys Include and validate all ID-based references as one consistent configuration set
Support bundle Diagnostics expose mutable keys from several stores Include job_id and bounded readable descriptors; add referential-integrity diagnostics
System health Checks metadata/repository assignments by key Detect duplicate/missing IDs and dangling ID references across active stores
Job deletion Deletes artifacts by Type ID and location patterns Delete only artifacts referencing selected job_id; require explicit artifact deletion confirmation
Factory reset Removes all stores independently Include all new ID-based stores/indexes without changing confirmation semantics
API logging/security context Logs job_key Log job_id and bounded readable descriptors; UUID is not secret

Dashboard and live-state rules

The configured job inventory is authoritative.

For every configured job_id, the dashboard combines:

  • current job metadata,
  • latest completed status for that job_id,
  • active runtime state for that job_id,
  • schedule and overdue state,
  • restore-test proof,
  • cached repository information.

Historical status records that no longer map to a configured job never create an additional dashboard card or affect current job counters.

If a migrated job has no new ID-based status yet, its latest unambiguously mapped legacy status may be shown with an indicator such as Last run before identity migration. After the first new run, the normal ID-based status takes precedence.

Reports, history, and historical labels

Reports and History keep continuity when a job is edited:

  • filtering and grouping use job_id,
  • the current job name is used for navigation,
  • each historical row may display the name, archive prefix, location, and repository captured for that run,
  • changing name or archive prefix does not split one logical job into multiple reports,
  • changing the current repository does not change report identity,
  • deleted jobs may remain available as historical jobs identified by former job_id,
  • ambiguous legacy records are shown separately as unassigned legacy history and are never attached by guesswork.

A historical repository_key is a run snapshot, not an active repository assignment.

Browse and Restore

Browse & Restore selects a job by job_id and resolves:

  • the one current repository assignment,
  • the ordered current and historical archive prefixes stored in archive_prefixes,
  • the current job display name.

The UUID never becomes part of the Borg archive prefix. Existing human-readable archives remain valid.

Repository assignment history is explicitly out of scope. After a repository change, the job uses only the new repository for Browse & Restore. Existing archives remain unchanged in the previous repository. Issue #452 provides the required warning and confirmation.

Restore tests

Restore-test policy and results belong to job_id.

Required behavior:

  • job name and archive-prefix changes do not reset proof status,
  • <job_key>.test becomes <job_id>.test,
  • scheduled and manual tests submit job_id,
  • overdue reminders and verification maps join by job_id,
  • result payload retains archive, repository, prefix, location, and display-name snapshots,
  • deleted jobs can retain historical restore-test results without appearing in the active plan.

Import/export identity rules

  • Import as a new job: always generate a new job_id, even if the exported source contains one.
  • Merge/update an explicitly selected existing job: retain the target installation's job_id.
  • Full configuration restore onto the same installation: preserve IDs when referential integrity is valid.
  • Importing a complete configuration onto another installation: preserve IDs only when there is no collision; otherwise require an explicit remap plan.
  • Duplicate job action: generate a new ID.
  • Never silently merge jobs because name, archive prefix, repository, or legacy key happens to match.
  • Export bundles include an ID reference map and validate that all selected dependent objects are present.

Migration design

Proposed migration ID:

immutable_job_id_v1

The migration runs before the scheduler, background writers, widget refresh, or writable APIs become available.

Detect phase

Inventory and validate:

  • all canonical job metadata,
  • legacy metadata filenames, job_key, backup_type, location, and existing archive_prefixes,
  • repository used_by / source_job_keys,
  • schedules,
  • active and archived .status files,
  • weekly snapshots,
  • restore-test files and policies,
  • restore active runs and restore history,
  • notification/reminder state,
  • runtime recovery state,
  • widget cache,
  • source manifests if present,
  • job-keyed logs or control metadata that must survive restart.

Detection returns required: true when any active job lacks a valid UUID, canonical metadata still uses a mutable identity, or a persistent active reference still uses a mutable key.

Planning phase

  1. Build the complete legacy-key-to-UUID plan before modifying data.
  2. Reuse an already journaled UUID if an interrupted attempt is resumed.
  3. Match current jobs by exact canonical legacy metadata key first.
  4. Determine the current explicit archive prefix from the ordered existing archive_prefixes when present; otherwise derive the legacy value once from backup_type by appending -backup.
  5. Preserve and normalize every valid existing prefix without inventing a second prefix-history model.
  6. Use explicit legacy aliases only when they produce one unambiguous match.
  7. Never infer identity from display name alone.
  8. Mark ambiguous or orphaned historical records as unassigned legacy data.
  9. Abort before writes if active configuration references are ambiguous or inconsistent.

Snapshot phase

Create a migration snapshot containing every affected persistent object, including:

  • job metadata directory,
  • repository and storage inventories as applicable,
  • schedules,
  • status and archived-status records,
  • weekly snapshots,
  • restore tests,
  • restore runs and restore-history index/details,
  • notification state,
  • runtime recovery state,
  • widget cache and persistent source manifests.

Write the migration plan and checksums into the snapshot. Do not include secrets beyond files already required by affected configuration, and never log secret values.

Apply phase

  1. Assign and persist one UUID per active job.
  2. Normalize archive_prefixes so the current complete prefix is first and prior prefixes remain de-duplicated.
  3. Remove Type ID, location, and mutable job_key as active canonical identity fields after dependent readers are converted. Preserve only bounded legacy aliases required by the migration contract.
  4. Rename canonical job metadata files to <job_id>.json.
  5. Convert repository current-assignment references to job IDs.
  6. Convert schedules to job IDs and regenerate cron entries.
  7. Add job_id to unambiguously mapped status records while retaining historical descriptors.
  8. Convert weekly snapshot keys to IDs and merge same-job/same-period entries deterministically.
  9. Rename and update restore-test result files.
  10. Add IDs to restore active runs and restore history records.
  11. Convert reminder/deduplication state without sending duplicate notifications.
  12. Convert runtime recovery records that must survive restart.
  13. Rebuild the widget cache from migrated canonical data.
  14. Invalidate all in-memory job, repository, status, and widget caches.
  15. Verify all active references before marking the migration applied.

Old historical filenames may remain unchanged if their payload has a valid job_id; readers must not depend on the filename. New files use ID-based lookup with readable diagnostic names.

Verification phase

Verify:

  • every active job has one unique valid UUID,
  • no UUID is shared by two jobs,
  • every active job has a non-empty ordered archive_prefixes list,
  • the first prefix is the exact prefix used for new archives,
  • no new archive path appends a hidden suffix to that prefix,
  • every active schedule references an existing job ID,
  • every current repository job reference resolves,
  • every restore-test policy/result resolves or is classified as legacy/unassigned,
  • all mapped statuses resolve to exactly one job ID,
  • current dashboard counts equal the configured active-job set,
  • Browse & Restore resolves the same current repository and all stored archive prefixes,
  • notification reminder state cannot generate duplicate reminders,
  • no writable service starts when verification fails.

Failure and retry behavior

  • Follow the existing startup migration contract and audit format.
  • Record pending, applied, skipped, failed, blocked, or not_applicable.
  • The operation is idempotent.
  • Re-running after success performs no changes.
  • Re-running after interruption uses the persisted migration plan and UUID mapping.
  • On the first failure, later migrations remain blocked and normal writes/scheduling stay disabled.
  • Do not automatically roll back the installed plugin version.
  • Keep the migration snapshot for manual recovery and expose details under System Health and Migration.

Compatibility boundary

During a defined transition period, API request parsing may accept a legacy job_key and resolve it through the migrated alias map.

This is boundary compatibility only:

  • all internal joins and newly persisted active data use job_id,
  • UI requests use job_id,
  • legacy-key use is logged as deprecated without exposing secrets,
  • ambiguous aliases fail explicitly,
  • new jobs do not create new job_key identities,
  • no permanent dual-write identity model remains.

Advantages

  • Editing a job no longer creates an apparent second job.
  • The wizard exposes the value that actually controls archive naming.
  • Users see the complete archive prefix and exact archive pattern before saving.
  • Dashboard and widget counts remain based on configured jobs.
  • Reports and History remain continuous across name, prefix, repository, and storage changes.
  • Restore tests and reminders remain attached to the logical job.
  • Repository links, schedules, runtime state, cancellation, and locks become stable.
  • Existing archive_prefixes continuity is preserved without repository history.
  • Future source manifests and other job-specific features gain a safe owner key.
  • Deleting one job cannot accidentally match another job's artifacts by filename pattern.
  • Referential-integrity checks become possible and meaningful.

Disadvantages and risks

  • This is a broad, high-risk migration touching nearly every job-dependent subsystem.
  • UUIDs are less readable in files and diagnostics, so UI and logs must resolve friendly labels.
  • Removing Type ID requires every remaining behavior to use explicit metadata instead of inferred semantics.
  • Old or already orphaned historical records may not be assignable automatically.
  • Import/export collision behavior becomes more complex.
  • External scripts or bookmarked internal API calls using job_key need a bounded compatibility window.
  • Status, report, and restore readers must support legacy artifacts during transition.
  • The implementation must be split into carefully reviewable stages while preserving one atomic migration contract.

Required test coverage

Identity and job lifecycle

  • UUID generation, validation, uniqueness, and immutability.
  • Create, edit, rename, archive-prefix change, repository change, and storage/location change.
  • Duplicate and delete behavior.
  • Job discovery from ID-named metadata.
  • No Type ID or derived mutable identity for new jobs.

Archive prefix and wizard

  • New jobs store the complete user-visible prefix exactly.
  • No hidden -backup suffix is appended.
  • Prefix preview matches the archive name produced by the runner.
  • Editing a prefix keeps the new prefix first and preserves prior prefixes.
  • Existing archive_prefixes migrate without loss or duplication.
  • Restore and prune use the explicit prefix list.
  • Repository-change confirmation remains owned by Warn and require confirmation before changing a job repository #452.

Migration

  • Fresh installation: migration not applicable.
  • Existing installation with one and multiple jobs.
  • Jobs with and without existing archive_prefixes.
  • Idempotent second run.
  • Interrupted run reuses the journaled UUID map.
  • Invalid and duplicate active references block migration.
  • Ambiguous historical records remain unassigned.
  • Snapshot completeness and audit output.
  • No later migration or writable service after failure.

Status, dashboard, and widgets

  • One card per configured job.
  • Edits do not create a second card.
  • Legacy status continuity before the first ID-based run.
  • Active runtime state overrides the last completed state for display.
  • Orphaned history does not affect active counters.
  • Widget cache and Homepage widget use the same identity rules.

Reports and History

  • Continuous time series across name, prefix, repository, and storage changes.
  • Historical labels remain accurate.
  • Deleted-job history remains accessible.
  • Unassigned legacy history is visible but not merged.
  • Weekly snapshots migrate without duplicate periods.

Browse, Restore, and Restore Tests

  • Archive filtering uses the complete ordered prefix list in the current repository.
  • Restore requests and persisted history use job_id.
  • Name and prefix edits keep restore history and restore proof.
  • Restore-test files, schedules, policies, reminders, and results migrate correctly.
  • Repository changes use only the current repository and never move archives.

Repositories, schedules, and runtime

  • Current repository references reconcile by ID.
  • Cron invokes jobs by ID.
  • Overdue detection joins by ID.
  • Runtime state, cancellation, locks, and recovery remain stable during edits.
  • Notifications and reminders do not duplicate after migration.

Import/export and diagnostics

  • New import, merge, full restore, duplicate, and collision cases.
  • Support bundle contains identity diagnostics without secrets.
  • System Health reports duplicate IDs, dangling references, and unresolved legacy data.

Acceptance criteria

  • Every active job has exactly one immutable UUID.
  • Metadata files are named <job_id>.json and discovered by ID.
  • Changing job name, archive prefix, repository, or storage does not change job_id.
  • All active job-dependent areas use job_id internally.
  • New and migrated jobs do not use Type ID, backup_type, location, or job_key as active identity.
  • The wizard replaces Type ID with the complete editable archive prefix.
  • The exact prefix shown in the wizard is used for new archive names without hidden additions.
  • archive_prefixes[0] is current and previous valid prefixes remain available in the current repository.
  • Dashboard and widgets display only configured jobs and exactly one entry per job.
  • Reports, History, Browse & Restore, restore history, and restore tests retain continuity across job edits within the defined current-repository boundary.
  • A repository change does not move archives or add repository history; the separate warning is tracked in Warn and require confirmation before changing a job repository #452.
  • Archive names remain readable and existing archives remain accessible in the current repository through stored prefixes.
  • Existing installations migrate automatically with snapshot, audit log, idempotence, and failure blocking.
  • No user data is silently deleted or assigned to an ambiguous job.
  • Legacy compatibility is limited to explicit read/resolve boundaries and is not a permanent second identity model.

Phased delivery plan

Issue #447 is delivered through one long-lived integration branch and one final implementation pull request:

  • Integration branch: codex/issue-447-immutable-job-ids
  • Pull request: one draft PR from the integration branch to main
  • Phase work: separate sub-issues and clearly separated commits on the shared branch
  • Merge boundary: no phase is merged or released independently
  • Stable release: a separate release PR only after the complete test candidate is explicitly approved

Parallel hotfixes continue on independent branches from main. After each hotfix is merged, current origin/main is merged into the #447 integration branch without rebasing or force-pushing. The final test candidate is created only after the last such synchronization.

Implementation phases

  1. Phase 1/9: Freeze the immutable job identity contract and migration fixture matrix #471 - Freeze the immutable identity contract and migration fixture matrix.
  2. Phase 2/9: Build the inactive migration planner, snapshot, journal, and verifier #472 - Build the inactive migration planner, snapshot, journal, and verifier.
  3. Phase 3/9: Introduce canonical ID-based job metadata and replace Type ID in the wizard #473 - Introduce canonical ID-based job metadata and replace Type ID in the wizard.
  4. Phase 4/9: Cut over job APIs, repository assignments, and schedules to job_id #474 - Cut over job APIs, repository assignments, and schedules to job_id.
  5. Phase 5/9: Cut over backup runtime, status, logs, locks, recovery, and notifications to job_id #475 - Cut over backup runtime, status, logs, locks, recovery, and notifications.
  6. Phase 6/9: Cut over dashboard, widgets, History, Reports, and overdue state to job_id #476 - Cut over dashboard, widgets, History, Reports, and overdue state.
  7. Phase 7/9: Cut over Browse and Restore, restore history, and restore tests to job_id #477 - Cut over Browse and Restore, restore history, and restore tests.
  8. Phase 8/9: Cut over import, configuration recovery, deletion, and identity diagnostics #478 - Cut over import, configuration recovery, deletion, and identity diagnostics.
  9. Phase 9/9: Activate immutable_job_id_v1 and qualify the end-to-end test candidate #479 - Activate immutable_job_id_v1 and qualify the end-to-end test candidate.

The ordering is intentional. Later phases depend on the contracts and converted stores from earlier phases. Completion of a sub-issue means its scoped code and automated tests are complete on the integration branch; it does not mean that the intermediate application state is installable or releasable.

Test-channel gate

Phases 1-8 do not require or justify a test-channel package merely because their individual code is complete. Until the real migration and every required reader/writer are integrated, an installed intermediate version may not start normally or may have no safe complete workflow to test.

The first test-channel candidate is produced only in #479 after all of the following are true:

  • the migration detects, plans, snapshots, applies and verifies representative legacy states;
  • invalid or ambiguous active states block before unsafe changes;
  • a successful migration releases startup, scheduler and writable APIs;
  • the migrated application can edit and schedule a job, complete a backup, preserve retention scope, browse archives, restore data and complete a restore test;
  • restart and retry are idempotent and reuse the same UUID mapping;
  • the complete automated migration matrix and full source preflight pass;
  • documentation, backup warning, failure guidance and release notes are complete.

If there is no safe end-to-end behavior for a user to verify, no test package is published. Development continues with unit, fixture and isolated integration tests instead.

Related dependency

Issue #470 remains blocked until #447 is fully completed because managed per-job exclusion files require the immutable job-owned storage contract.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area::diagnosticsLogging, support bundles, diagnostics, and troubleshootingarea::notificationsNotifications, reminders, email, ntfy and Unraid notification handlingarea::repositoriesRepository storage, Borg maintenance, and remote transport handlingarea::restoreRestore and Browse & Restorearea::uiUser interface, visual design, and frontend interaction patternsarea::wizardJob wizard and guided job creationimpact::user-visibleVisible effect for plugin usersrelease-note::yesInclude in user-facing release notestype::featureNew user-facing or plugin feature

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions