* enhance(#3638): render bracket IDs on display surfaces Gate progress, stats, manager, and statusline projections on the bracket convention; validate phase_id_convention and single-source the convention card. Forward note: the uat.cts bracket co-change remains deliberately deferred to its owning slice. * chore(#3638): point the changeset at PR #4111 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#3638): close bracket display review gaps * docs(#3638): register phase display modules * chore(#3638): re-trigger CI after macOS shard SIGTERM `full test (macos-latest, 24, shard 3/3)` failed on 20ce98cd1 in `tests/lint-compiled-artifact-sync.test.cjs` — the spawned `scripts/lint-compiled-artifact-sync.cjs` was killed at 60024ms (`exited null (signal SIGTERM)`, stdout and stderr both empty), 24ms past the test's own `TSC_COMPILE_TIMEOUT_MS`. That is the failure mode the constant's comment already documents ("under CI shard load that compile can exceed the budget, dying to a SIGTERM with empty piped stdout"). No content change; this empty commit exists only to re-run the matrix, since re-running a job needs write access on the upstream repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
/**
|
|
* Phase-ID display adapters (#3638 / ADR-612 PR-5).
|
|
*
|
|
* STATE.md and milestone metadata still expose the legacy `vN.0` marker on
|
|
* this stacked base. Display surfaces need to translate that metadata into a
|
|
* bracket identity without growing another renderer. These pure adapters do
|
|
* only the boundary normalization, then delegate identity parsing/rendering to
|
|
* phase-id.cts's canonical pair.
|
|
*/
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
import phaseId = require('./phase-id.cjs');
|
|
const { parsePhaseId, renderMilestoneId, renderPhaseId } = phaseId;
|
|
|
|
function canonicalNumeric(value: string): string {
|
|
let offset = 0;
|
|
while (offset < value.length - 1 && value[offset] === '0') offset++;
|
|
const stripped = value.slice(offset);
|
|
return stripped.length < 2 ? stripped.padStart(2, '0') : stripped;
|
|
}
|
|
|
|
function isDigits(value: string): boolean {
|
|
return value.length > 0
|
|
&& [...value].every(char => char >= '0' && char <= '9');
|
|
}
|
|
|
|
function milestoneToken(value: unknown): string | null {
|
|
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
|
const raw = String(value).replace(/^v/i, '');
|
|
const parts = raw.split('.');
|
|
if (parts.length > 2 || parts.some(part => !isDigits(part))) return null;
|
|
return canonicalNumeric(parts[0]);
|
|
}
|
|
|
|
function phaseToken(value: unknown): string | null {
|
|
if (typeof value !== 'string' && typeof value !== 'number') return null;
|
|
const raw = String(value);
|
|
const parts = raw.split('.');
|
|
if (parts.length > 2 || parts.some(part => !isDigits(part))) return null;
|
|
return parts.map(canonicalNumeric).join('.');
|
|
}
|
|
|
|
/**
|
|
* Render a bracket phase display from legacy milestone metadata.
|
|
* Returns null for incomplete or invalid metadata so cosmetic callers can
|
|
* degrade without breaking a command/statusline render.
|
|
*/
|
|
function renderBracketPhaseDisplay(
|
|
milestone: unknown,
|
|
phase: unknown,
|
|
projectCode: unknown,
|
|
): string | null {
|
|
const project = typeof projectCode === 'string' ? projectCode : '';
|
|
const mm = milestoneToken(milestone);
|
|
const pp = phaseToken(phase);
|
|
if (!project || mm === null || pp === null) return null;
|
|
|
|
try {
|
|
return renderPhaseId(parsePhaseId(`${project}.${mm}-${pp}`));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Render only the bracket milestone label, still through canonical parsing. */
|
|
function renderBracketMilestoneDisplay(
|
|
milestone: unknown,
|
|
projectCode: unknown,
|
|
): string | null {
|
|
const project = typeof projectCode === 'string' ? projectCode : '';
|
|
const mm = milestoneToken(milestone);
|
|
if (!project || mm === null) return null;
|
|
|
|
try {
|
|
// The sentinel phase is a validation vehicle, not a rendering artifact.
|
|
return renderMilestoneId(parsePhaseId(`${project}.${mm}-00`));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export = {
|
|
renderBracketPhaseDisplay,
|
|
renderBracketMilestoneDisplay,
|
|
};
|