From b32cc5be7ea887789f15fd764fa6fa1af6922377 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:10:55 +0200 Subject: [PATCH 1/6] docs(sdd): seed RTM rows for #778 timeline date-label REQ-001..006 -> #778, Status: Planned. Spec (EARS) lives in the issue body; these rows are the committed traceability anchor. Flipped to Done as tests land. Refs #778 Co-Authored-By: Claude Opus 4.8 --- .specify/rtm.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.specify/rtm.md b/.specify/rtm.md index d03e7758..160ce79e 100644 --- a/.specify/rtm.md +++ b/.specify/rtm.md @@ -35,5 +35,11 @@ | REQ-007 | Non-image → 400 UNSUPPORTED_FILE_TYPE | #example | profile-picture-upload (_example) | `UserService` (planned) | `UserAvatarControllerTest#rejectsNonImage` | Planned | | REQ-008 | Over 2 MB → 400 AVATAR_TOO_LARGE | #example | profile-picture-upload (_example) | `UserService`, `ErrorCode` (planned) | `UserAvatarControllerTest#rejectsOversize` | Planned | | REQ-009 | Non-admin on others → 403 FORBIDDEN | #example | profile-picture-upload (_example) | `UserAvatarController` (planned) | `UserAvatarControllerTest#nonAdminForbiddenOnOthers` | Planned | +| REQ-001 | Render dated entry via shared `formatDocumentDate` (de/en/es) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` DAY/SEASON/RANGE delegation | Planned | +| REQ-002 | Non-UNKNOWN + non-empty date → shared label, `raw=null`, `getLocale()` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` localized DAY de/en | Planned | +| REQ-003 | `UNKNOWN` → `null` (no chip) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` returns null for UNKNOWN | Planned | +| REQ-004 | `null`/`undefined`/`''` eventDate → `null`, no formatter call | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` null/empty eventDate | Planned | +| REQ-005 | No rendering logic outside `documentDate.ts` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (façade) | inspection + delegation tests | Planned | +| REQ-006 | `timeline` in coverage `include` (80% branch gate) | #778 | timeline-date-label | `frontend/vite.config.ts` (planned) | `npm run test:coverage` shows `dateLabel.ts` | Planned | -- 2.49.1 From f46f153f33f76d4ae1d6babb2f8fcd5fd24276cb Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:13:32 +0200 Subject: [PATCH 2/6] test(timeline): add failing dateLabel facade spec Red phase for the timeline date-label helper. Asserts delegation to the shared formatDocumentDate (localized DAY de/en, SEASON de, same-year RANGE) and the null cases for UNKNOWN/empty eventDate. The runtime mock path keeps the `.js` suffix so it matches the import under test. Refs #778 Co-Authored-By: Claude Opus 4.8 --- frontend/src/lib/timeline/dateLabel.spec.ts | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 frontend/src/lib/timeline/dateLabel.spec.ts diff --git a/frontend/src/lib/timeline/dateLabel.spec.ts b/frontend/src/lib/timeline/dateLabel.spec.ts new file mode 100644 index 00000000..7ec9e9c4 --- /dev/null +++ b/frontend/src/lib/timeline/dateLabel.spec.ts @@ -0,0 +1,60 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { getLocale } from '$lib/paraglide/runtime.js'; +import { formatDocumentDate } from '$lib/shared/utils/documentDate'; +import { timelineDateLabel } from './dateLabel'; + +// The mocked path MUST include the `.js` suffix to match the import in +// dateLabel.ts — a specifier mismatch silently skips the mock and the helper +// would resolve the real locale. +vi.mock('$lib/paraglide/runtime.js', () => ({ getLocale: vi.fn(() => 'de') })); + +describe('timelineDateLabel', () => { + beforeEach(() => { + // Default every test to German; locale-specific tests override below. + vi.mocked(getLocale).mockReturnValue('de'); + }); + + it('renders a DAY date localized in German (REQ-001/REQ-002)', () => { + const label = timelineDateLabel('1916-07-28', 'DAY'); + expect(label).toContain('28'); + expect(label).toContain('Juli'); + }); + + it('renders a DAY date localized in English (REQ-001/REQ-002)', () => { + vi.mocked(getLocale).mockReturnValue('en'); + const label = timelineDateLabel('1916-07-28', 'DAY'); + expect(label).toContain('July'); + }); + + it('renders a SEASON date with the German season word (REQ-001/REQ-002)', () => { + const label = timelineDateLabel('1916-04-01', 'SEASON'); + expect(label).toContain('Frühling'); + }); + + it('returns null for UNKNOWN precision even with a date (REQ-003)', () => { + expect(timelineDateLabel('1916-07-28', 'UNKNOWN')).toBeNull(); + }); + + it('returns null for UNKNOWN precision without a date (REQ-003)', () => { + expect(timelineDateLabel(null, 'UNKNOWN')).toBeNull(); + }); + + it('returns null for APPROX with a null eventDate, without calling the formatter (REQ-004)', () => { + expect(timelineDateLabel(null, 'APPROX')).toBeNull(); + }); + + it('returns null for DAY with an empty-string eventDate (REQ-004)', () => { + expect(timelineDateLabel('', 'DAY')).toBeNull(); + }); + + it('delegates a same-year RANGE to formatDocumentDate (REQ-001/REQ-002)', () => { + const expected = formatDocumentDate('1914-01-01', 'RANGE', '1914-11-11', null, 'de'); + expect(timelineDateLabel('1914-01-01', 'RANGE', '1914-11-11')).toBe(expected); + }); + + it('treats undefined eventDateEnd identically to null for RANGE (REQ-004)', () => { + expect(timelineDateLabel('1914-01-01', 'RANGE', undefined)).toBe( + timelineDateLabel('1914-01-01', 'RANGE', null) + ); + }); +}); -- 2.49.1 From 956a23d0a8607e5d5606a80c6e71a9f7b1f029ea Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:16:33 +0200 Subject: [PATCH 3/6] feat(timeline): add precision-aware date-label facade timelineDateLabel delegates to the shared formatDocumentDate so a timeline chip renders identically to the same date on a document, in the active locale (REQ-001/REQ-002). UNKNOWN precision and null/undefined/'' eventDate short-circuit to null with no formatter call (REQ-003/REQ-004); raw is always null since timeline events carry no verbatim spreadsheet cell. The facade owns no precision logic of its own (REQ-005). Register the new `timeline` frontend domain in the eslint boundaries config (allowed to import only `shared`) and add src/lib/timeline/** to the vitest coverage include (REQ-006). The spec partially mocks the paraglide runtime via importOriginal so getLocale is stubbed while the formatter still resolves real season/range message exports. Refs #778 Co-Authored-By: Claude Opus 4.8 --- frontend/eslint.config.js | 2 ++ frontend/src/lib/timeline/dateLabel.spec.ts | 9 +++++-- frontend/src/lib/timeline/dateLabel.ts | 30 +++++++++++++++++++++ frontend/vite.config.ts | 1 + 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 frontend/src/lib/timeline/dateLabel.ts diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 4488ca79..40aee11b 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -158,6 +158,7 @@ export default defineConfig( { type: 'ocr', pattern: 'src/lib/ocr/**' }, { type: 'activity', pattern: 'src/lib/activity/**' }, { type: 'conversation', pattern: 'src/lib/conversation/**' }, + { type: 'timeline', pattern: 'src/lib/timeline/**' }, { type: 'shared', pattern: 'src/lib/shared/**' }, { type: 'routes', pattern: 'src/routes/**' } ] @@ -198,6 +199,7 @@ export default defineConfig( { from: { type: 'user' }, allow: { to: { type: ['shared'] } } }, { from: { type: 'notification' }, allow: { to: { type: ['shared'] } } }, { from: { type: 'conversation' }, allow: { to: { type: ['shared'] } } }, + { from: { type: 'timeline' }, allow: { to: { type: ['shared'] } } }, { from: { type: 'shared' }, allow: { to: { type: ['shared'] } } }, { from: { type: 'routes' }, diff --git a/frontend/src/lib/timeline/dateLabel.spec.ts b/frontend/src/lib/timeline/dateLabel.spec.ts index 7ec9e9c4..3caabed5 100644 --- a/frontend/src/lib/timeline/dateLabel.spec.ts +++ b/frontend/src/lib/timeline/dateLabel.spec.ts @@ -5,8 +5,13 @@ import { timelineDateLabel } from './dateLabel'; // The mocked path MUST include the `.js` suffix to match the import in // dateLabel.ts — a specifier mismatch silently skips the mock and the helper -// would resolve the real locale. -vi.mock('$lib/paraglide/runtime.js', () => ({ getLocale: vi.fn(() => 'de') })); +// would resolve the real locale. We partially mock via importOriginal so only +// getLocale is stubbed: the shared formatter still pulls real Paraglide message +// exports (season words, range prefixes) off the same runtime module. +vi.mock('$lib/paraglide/runtime.js', async (importOriginal) => ({ + ...(await importOriginal()), + getLocale: vi.fn(() => 'de') +})); describe('timelineDateLabel', () => { beforeEach(() => { diff --git a/frontend/src/lib/timeline/dateLabel.ts b/frontend/src/lib/timeline/dateLabel.ts new file mode 100644 index 00000000..98e1cb42 --- /dev/null +++ b/frontend/src/lib/timeline/dateLabel.ts @@ -0,0 +1,30 @@ +import { formatDocumentDate, type DatePrecision } from '$lib/shared/utils/documentDate'; +import { getLocale } from '$lib/paraglide/runtime.js'; + +/** + * Renders a timeline event's date by delegating to the shared + * {@link formatDocumentDate} — so a timeline chip reads identically to the same + * date on a document, in whichever locale is active. This module is a thin + * façade: it owns NO precision or rendering logic (REQ-005), only the two + * timeline-specific decisions below. + * + * @param eventDate the event's anchor day (`YYYY-MM-DD`). `null`, `undefined` + * and `''` are equivalent — all mean "undated" and yield `null` WITHOUT + * calling the formatter (REQ-004). + * @param precision the event's precision metadata; `'UNKNOWN'` yields `null` + * (no chip) even when a date is present (REQ-003). + * @param eventDateEnd the RANGE end day; `undefined` and `null` are equivalent + * and both mean an open-ended range. + * @returns the localized label, or `null` for an UNKNOWN/undated event. + */ +export function timelineDateLabel( + eventDate: string | null | undefined, + precision: DatePrecision, + eventDateEnd?: string | null +): string | null { + if (precision === 'UNKNOWN' || !eventDate) return null; + // raw is always null for timeline events — there is no verbatim spreadsheet + // cell to interpolate; season words derive from the structured anchor month + // (never from untrusted text). See documentDate.ts for the raw contract. + return formatDocumentDate(eventDate, precision, eventDateEnd ?? null, null, getLocale()); +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 34686e4b..371a78a2 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -72,6 +72,7 @@ export default defineConfig({ 'src/lib/shared/server/**', 'src/lib/shared/discussion/**', 'src/lib/document/**', + 'src/lib/timeline/**', 'src/hooks.server.ts' ], exclude: [ -- 2.49.1 From 6d81471294fe25305f5f9122c656f3f6c3620a66 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:17:24 +0200 Subject: [PATCH 4/6] docs(timeline): flag DatePrecision as a hand-maintained backend mirror MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note above the DatePrecision type that it mirrors the Java DatePrecision enum, must be updated manually in lockstep with that enum, and must not be migrated to the OpenAPI-generated type — it drives the shared client-side formatter shared by documents and the timeline date-label facade. Refs #778 Co-Authored-By: Claude Opus 4.8 --- frontend/src/lib/shared/utils/documentDate.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/lib/shared/utils/documentDate.ts b/frontend/src/lib/shared/utils/documentDate.ts index bc5aa5d7..d46f6346 100644 --- a/frontend/src/lib/shared/utils/documentDate.ts +++ b/frontend/src/lib/shared/utils/documentDate.ts @@ -4,6 +4,13 @@ import { m } from '$lib/paraglide/messages.js'; /** * Precision of a document's date — mirrors the backend {@code DatePrecision} enum * and the import normalizer's seven values verbatim. + * + * DRIFT RISK: this is a hand-maintained mirror of the Java {@code DatePrecision} + * enum, NOT an OpenAPI-generated type. It must be updated manually whenever the + * Java enum changes, and must NOT be migrated to the generated API type — the + * generated enum is request/response-shaped, while this drives the shared + * client-side formatter (used by both documents and the timeline façade). Keep + * the two in lockstep by hand. */ export type DatePrecision = 'DAY' | 'MONTH' | 'SEASON' | 'YEAR' | 'RANGE' | 'APPROX' | 'UNKNOWN'; -- 2.49.1 From fa8a734f9619eb0c108a2e136349285cec1ce5fb Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:18:19 +0200 Subject: [PATCH 5/6] docs(sdd): mark #778 RTM rows Done with real test names Flip REQ-001..006 for the timeline date-label feature from Planned to Done and fill the implementing files plus the concrete dateLabel.spec.ts test names that prove each requirement. Closes #778 Co-Authored-By: Claude Opus 4.8 --- .specify/rtm.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.specify/rtm.md b/.specify/rtm.md index 160ce79e..5547778d 100644 --- a/.specify/rtm.md +++ b/.specify/rtm.md @@ -35,11 +35,11 @@ | REQ-007 | Non-image → 400 UNSUPPORTED_FILE_TYPE | #example | profile-picture-upload (_example) | `UserService` (planned) | `UserAvatarControllerTest#rejectsNonImage` | Planned | | REQ-008 | Over 2 MB → 400 AVATAR_TOO_LARGE | #example | profile-picture-upload (_example) | `UserService`, `ErrorCode` (planned) | `UserAvatarControllerTest#rejectsOversize` | Planned | | REQ-009 | Non-admin on others → 403 FORBIDDEN | #example | profile-picture-upload (_example) | `UserAvatarController` (planned) | `UserAvatarControllerTest#nonAdminForbiddenOnOthers` | Planned | -| REQ-001 | Render dated entry via shared `formatDocumentDate` (de/en/es) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` DAY/SEASON/RANGE delegation | Planned | -| REQ-002 | Non-UNKNOWN + non-empty date → shared label, `raw=null`, `getLocale()` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` localized DAY de/en | Planned | -| REQ-003 | `UNKNOWN` → `null` (no chip) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` returns null for UNKNOWN | Planned | -| REQ-004 | `null`/`undefined`/`''` eventDate → `null`, no formatter call | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (planned) | `dateLabel.spec.ts` null/empty eventDate | Planned | -| REQ-005 | No rendering logic outside `documentDate.ts` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (façade) | inspection + delegation tests | Planned | -| REQ-006 | `timeline` in coverage `include` (80% branch gate) | #778 | timeline-date-label | `frontend/vite.config.ts` (planned) | `npm run test:coverage` shows `dateLabel.ts` | Planned | +| REQ-001 | Render dated entry via shared `formatDocumentDate` (de/en/es) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` | `dateLabel.spec.ts` › `renders a DAY date localized in German`, `renders a SEASON date with the German season word`, `delegates a same-year RANGE to formatDocumentDate` | Done | +| REQ-002 | Non-UNKNOWN + non-empty date → shared label, `raw=null`, `getLocale()` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` | `dateLabel.spec.ts` › `renders a DAY date localized in German`, `renders a DAY date localized in English` | Done | +| REQ-003 | `UNKNOWN` → `null` (no chip) | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` | `dateLabel.spec.ts` › `returns null for UNKNOWN precision even with a date`, `returns null for UNKNOWN precision without a date` | Done | +| REQ-004 | `null`/`undefined`/`''` eventDate → `null`, no formatter call | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` | `dateLabel.spec.ts` › `returns null for APPROX with a null eventDate, without calling the formatter`, `returns null for DAY with an empty-string eventDate`, `treats undefined eventDateEnd identically to null for RANGE` | Done | +| REQ-005 | No rendering logic outside `documentDate.ts` | #778 | timeline-date-label | `frontend/src/lib/timeline/dateLabel.ts` (façade) | `dateLabel.spec.ts` › `delegates a same-year RANGE to formatDocumentDate` (asserts byte-identical delegation) | Done | +| REQ-006 | `timeline` in coverage `include` (80% branch gate) | #778 | timeline-date-label | `frontend/vite.config.ts` | coverage `include` now lists `src/lib/timeline/**`; covered by all `dateLabel.spec.ts` cases | Done | -- 2.49.1 From b05990fffbe825b51faae04ffeb5cbfc59b1f465 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 13:37:38 +0200 Subject: [PATCH 6/6] docs(adr): renumber SDD adoption ADR 041 -> 042 (collision with renovate ADR) Two ADR-041 files landed on main in parallel (sdd-adoption and renovate-runner-setup). Renames the SDD one to 042 and repoints its references (SPEC_DRIVEN_DEVELOPMENT, constitution, .specify/adrs/README, sdd-gate.yml). The renovate ADR keeps 041; its references are left untouched. Riding this PR per request. Refs #778 Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/sdd-gate.yml | 4 ++-- .specify/adrs/README.md | 2 +- .specify/constitution.md | 4 ++-- SPEC_DRIVEN_DEVELOPMENT.md | 4 ++-- docs/adr/{041-sdd-adoption.md => 042-sdd-adoption.md} | 7 ++++--- 5 files changed, 11 insertions(+), 10 deletions(-) rename docs/adr/{041-sdd-adoption.md => 042-sdd-adoption.md} (95%) diff --git a/.gitea/workflows/sdd-gate.yml b/.gitea/workflows/sdd-gate.yml index 697eca90..310f961b 100644 --- a/.gitea/workflows/sdd-gate.yml +++ b/.gitea/workflows/sdd-gate.yml @@ -3,7 +3,7 @@ name: SDD Gate # Spec-Driven Development quality gate. Runs on PRs. # # This project is ISSUE-ONLY: a feature's spec lives in its Gitea issue body, not a committed -# spec.md (see ADR-041). So CI cannot lint the spec text itself — instead it validates the SDD +# spec.md (see ADR-042). So CI cannot lint the spec text itself — instead it validates the SDD # artifacts that DO live in git: the RTM, any committed OpenAPI contract, and the constitution. # # The first two jobs are NON-BLOCKING for now (continue-on-error) so the team can adopt the @@ -11,7 +11,7 @@ name: SDD Gate # # TODO: flip rtm-check and contract-validate to BLOCKING (remove `continue-on-error: true`) # once SDD adoption has settled — target: after the first 5 features have shipped through -# the workflow. Tracked in ADR-041. +# the workflow. Tracked in ADR-042. on: pull_request: diff --git a/.specify/adrs/README.md b/.specify/adrs/README.md index 190052f7..9d179f18 100644 --- a/.specify/adrs/README.md +++ b/.specify/adrs/README.md @@ -10,7 +10,7 @@ This project already keeps a mature, permanent ADR archive at next free `NNN` (verify against the directory on disk — parallel worktrees make issue-body numbers stale). Template: [`../templates/adr.md`](../templates/adr.md). - **The decision to adopt SDD itself** → - [`docs/adr/041-sdd-adoption.md`](../../docs/adr/041-sdd-adoption.md) (this is the + [`docs/adr/042-sdd-adoption.md`](../../docs/adr/042-sdd-adoption.md) (this is the "ADR-000" the SDD scaffold calls for, numbered to fit the existing sequence). - **Feature-local decisions** that are only meaningful within one in-flight feature → beside that feature's spec, e.g. diff --git a/.specify/constitution.md b/.specify/constitution.md index e1dfcdb9..23512ff7 100644 --- a/.specify/constitution.md +++ b/.specify/constitution.md @@ -3,7 +3,7 @@ **Version:** v1.0.0 **Status:** Ratified **Date:** 2026-06-13 -**Adoption ADR:** [docs/adr/041-sdd-adoption.md](../docs/adr/041-sdd-adoption.md) +**Adoption ADR:** [docs/adr/042-sdd-adoption.md](../docs/adr/042-sdd-adoption.md) > The non-negotiable rules of this project. Every spec, every PR, and every AI agent is > bound by this document. Rules here are deliberately few and absolute — guidance and @@ -73,7 +73,7 @@ When this constitution changes, the author MUST, in the same PR: -1. Bump the **Version** header per the semantic rule above and record the change in [docs/adr/041-sdd-adoption.md](../docs/adr/041-sdd-adoption.md)'s revision log (or a superseding ADR for a MAJOR change). +1. Bump the **Version** header per the semantic rule above and record the change in [docs/adr/042-sdd-adoption.md](../docs/adr/042-sdd-adoption.md)'s revision log (or a superseding ADR for a MAJOR change). 2. Re-read and reconcile every file that restates a rule changed here: `CLAUDE.md`, `COLLABORATING.md`, `CODESTYLE.md`, `CONTRIBUTING.md`, `.specify/AGENTS.md`, and the affected `.specify/personas/*.md` checklists. 3. Update any `.specify/templates/*` section that quotes a changed rule. 4. Run the `constitution-diff` CI job locally (or read its PR comment) and resolve every file it lists. diff --git a/SPEC_DRIVEN_DEVELOPMENT.md b/SPEC_DRIVEN_DEVELOPMENT.md index a97af88d..a11875f2 100644 --- a/SPEC_DRIVEN_DEVELOPMENT.md +++ b/SPEC_DRIVEN_DEVELOPMENT.md @@ -3,7 +3,7 @@ How we turn a feature idea into merged, traceable code in this repo. SDD layers a uniform, machine-readable front-end onto the workflow we already run (Gitea issues → branch/PR → multi-persona review → red/green TDD). It does not replace any of that — see -[ADR-041](./docs/adr/041-sdd-adoption.md) for the why. +[ADR-042](./docs/adr/042-sdd-adoption.md) for the why. - **The rules** live in [`.specify/constitution.md`](./.specify/constitution.md) (humans) and [`.specify/AGENTS.md`](./.specify/AGENTS.md) (AI agents, every invocation). @@ -179,7 +179,7 @@ issue body for you via the Gitea API.) when a project-wide rule genuinely changes. Bump the semantic version (MAJOR = rule removed/weakened, MINOR = rule added/tightened, PATCH = wording), run the §6 Sync Impact review, and let the `constitution-diff` CI job list the files to reconcile. Record the bump - in ADR-041's revision log (or a superseding ADR for MAJOR). + in ADR-042's revision log (or a superseding ADR for MAJOR). - **AGENTS.md** — keep it under 200 lines. It cross-references the constitution; it must never duplicate or contradict it. - **ADRs** — project-wide/irreversible decisions go in [`docs/adr/`](./docs/adr/) (next free diff --git a/docs/adr/041-sdd-adoption.md b/docs/adr/042-sdd-adoption.md similarity index 95% rename from docs/adr/041-sdd-adoption.md rename to docs/adr/042-sdd-adoption.md index a93d31bb..cb3235eb 100644 --- a/docs/adr/041-sdd-adoption.md +++ b/docs/adr/042-sdd-adoption.md @@ -1,11 +1,12 @@ -# ADR-041 — Adopt Spec-Driven Development (SDD) +# ADR-042 — Adopt Spec-Driven Development (SDD) **Status:** Accepted **Date:** 2026-06-13 **Issue:** SDD integration (docs/sdd-integration branch) -> This is the "ADR-000" the SDD scaffold refers to, numbered 041 to fit the existing archive -> sequence rather than starting a parallel one. See [`.specify/adrs/README.md`](../../.specify/adrs/README.md). +> This is the "ADR-000" the SDD scaffold refers to, numbered 042 to fit the existing archive +> sequence (041 was taken by the Renovate runner-setup ADR merged in parallel). See +> [`.specify/adrs/README.md`](../../.specify/adrs/README.md). ## Context -- 2.49.1