Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public static IServiceCollection AddApplication(this IServiceCollection services
// Custom-field ACL (T-031 §4.4): validates inbound custom fields against the tenant's schema.
services.AddScoped<Governance.ArtifactFieldSchema.IArtifactCustomFieldAcl, Governance.ArtifactFieldSchema.ArtifactCustomFieldAcl>();

// Core artifact contract source (T-036 §5 / plan item 6): the seam for where the phase
// artifact profiles come from. Today it is the provisional Tracker-held stand-in (GAP-004);
// swap this single registration for a Core-sync source once Evolith Core publishes the catalog.
services.AddSingleton<Governance.ArtifactFieldSchema.IPhaseArtifactProfileSource, Governance.ArtifactFieldSchema.StandInPhaseArtifactProfileSource>();

// Intake hardening: UMS RACI resolution + handshake credential verification.
services.AddSingleton<IUmsDirectoryPort, DevUmsDirectoryPort>();
services.AddScoped<IStakeholderResolver, StakeholderResolver>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Tracker.Application.Governance.ArtifactFieldSchema;

/// <summary>
/// Provenance of the phase-artifact profile set (ADR T-036 §5). Tracker must source the Core
/// artifact contract from Evolith Core; until Core exposes it (GAP-004) Tracker serves a
/// Tracker-held stand-in that is explicitly marked as a provisional mirror of the Core standard.
/// </summary>
public static class PhaseArtifactProfileSourceKind
{
/// <summary>Provisional Tracker-held mirror of the Core standard (GAP-004). Replaced by sync.</summary>
public const string StandIn = "core-standin";

/// <summary>Synced from the authoritative Evolith Core catalog (GAP-004 resolved).</summary>
public const string CoreSync = "core-sync";
}

/// <summary>
/// The seam that decouples Tracker from where the Core phase-artifact contract comes from
/// (ADR T-036 §5 / plan item 6). Today the only implementation is the provisional stand-in
/// (<see cref="StandInPhaseArtifactProfileSource"/>); when Evolith Core publishes the catalog
/// (GAP-004 resolved) a Core-sync implementation replaces the DI registration — nothing else in
/// the gate-governance stack changes.
/// </summary>
public interface IPhaseArtifactProfileSource
{
/// <summary>Provenance of the served profiles (see <see cref="PhaseArtifactProfileSourceKind"/>).</summary>
string Source { get; }

/// <summary>The Core-authoritative base artifacts per SDLC phase.</summary>
IReadOnlyList<PhaseArtifactProfileDto> GetProfiles();
}

/// <summary>
/// Provisional Core mirror (GAP-004): serves <see cref="PhaseArtifactCatalog"/> as the stand-in
/// while Evolith Core is unreachable. Every profile is stamped <c>core-standin</c> so consumers can
/// flag it as not-yet-synced. Swap for a Core-sync source once the Core contract endpoint exists.
/// </summary>
internal sealed class StandInPhaseArtifactProfileSource : IPhaseArtifactProfileSource
{
public string Source => PhaseArtifactProfileSourceKind.StandIn;

public IReadOnlyList<PhaseArtifactProfileDto> GetProfiles() =>
PhaseArtifactCatalog.Phases
.Select(phase => new PhaseArtifactProfileDto
{
Phase = phase,
Source = PhaseArtifactProfileSourceKind.StandIn,
Artifacts = PhaseArtifactCatalog.ByPhase[phase]
.Select(a => new PhaseArtifactDto
{
ArtifactKind = a.ArtifactKind,
Label = a.Label,
Required = a.Required,
})
.ToList(),
})
.ToList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ public sealed class PhaseArtifactDto
public sealed class PhaseArtifactProfileDto
{
public string Phase { get; init; } = string.Empty;

/// <summary>
/// Provenance of this profile set (ADR T-036 §5): <c>core-standin</c> (provisional Tracker
/// mirror, GAP-004) or <c>core-sync</c> (synced from Evolith Core). Lets consumers flag a
/// not-yet-synced catalog. See <see cref="PhaseArtifactProfileSourceKind"/>.
/// </summary>
public string Source { get; init; } = PhaseArtifactProfileSourceKind.StandIn;

public IReadOnlyList<PhaseArtifactDto> Artifacts { get; init; } = new List<PhaseArtifactDto>();
}

Expand All @@ -86,23 +94,13 @@ public sealed record GetPhaseArtifactProfilesQuery() : IQuery<IReadOnlyList<Phas
internal sealed class GetPhaseArtifactProfilesQueryHandler
: IQueryHandler<GetPhaseArtifactProfilesQuery, IReadOnlyList<PhaseArtifactProfileDto>>
{
// T-036 §5 / plan item 6: read through the Core-source seam, not the static catalog directly,
// so the provisional stand-in can be swapped for a Core-sync source with no handler change.
private readonly IPhaseArtifactProfileSource _source;

public GetPhaseArtifactProfilesQueryHandler(IPhaseArtifactProfileSource source) => _source = source;

public Task<IReadOnlyList<PhaseArtifactProfileDto>> Handle(
GetPhaseArtifactProfilesQuery request, CancellationToken cancellationToken)
{
var result = PhaseArtifactCatalog.Phases
.Select(phase => new PhaseArtifactProfileDto
{
Phase = phase,
Artifacts = PhaseArtifactCatalog.ByPhase[phase]
.Select(a => new PhaseArtifactDto
{
ArtifactKind = a.ArtifactKind,
Label = a.Label,
Required = a.Required,
})
.ToList(),
})
.ToList();
return Task.FromResult<IReadOnlyList<PhaseArtifactProfileDto>>(result);
}
=> Task.FromResult(_source.GetProfiles());
}
5 changes: 5 additions & 0 deletions src/apps/tracker-web/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ export interface PhaseArtifactDto {
/** GET /api/phase-artifact-profiles — Core base artifacts per phase (PRD required in Discovery, …). */
export interface PhaseArtifactProfileDto {
phase: string;
/**
* Provenance of the catalog (ADR T-036 §5): 'core-standin' = provisional Tracker mirror while
* Evolith Core is unreachable (GAP-004); 'core-sync' = synced from Core. Absent on legacy responses.
*/
source?: string | null;
artifacts: PhaseArtifactDto[];
}

Expand Down
Loading
Loading