Skip to content

Identify decision records by their GitHub tracking number, not a global counter - #114

Merged
erseco merged 10 commits into
mainfrom
docs/architecture-tracking-number-identifiers
Aug 5, 2026
Merged

Identify decision records by their GitHub tracking number, not a global counter#114
erseco merged 10 commits into
mainfrom
docs/architecture-tracking-number-identifiers

Conversation

@erseco

@erseco erseco commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Update — this description predates two changes

The validator is now one shared TypeScript file, byte-identical across
core and all four plugins: research/tools/architecture-records.mts. It runs under Bun in
exelearning/exelearning, where bun test covers it, and under the Node that
ships on the CI image here — no setup step, no dependency. Anything below that
names a per-repository PHP or Python validator is superseded. Repository-specific
values live in architecture-records.json: paths and the record prefix only.

Do not edit the copy in this repository. Fix it in core and re-copy, or the
copies drift apart — the exact failure this convention exists to prevent.

There is no remaining numbering debt. The 18 bootstrap records that had no
verifiable tracking number are now DEC-0-01DEC-0-18. 0 is a sentinel
for records that predate tracking: GitHub numbers issues and pull requests from
1, so it can never collide, and unlike 1 it does not claim a pull request that
exists and is about something else. check now reports 64 records (18 from
repository bootstrap), 0 changes
— nothing is left on the retired numbering.

Frontmatter keys are now English (titulo -> title, estado -> status,
relacionados -> related.adrs, herramienta_ia -> ai_assistance), so the
schema matches every other repository exactly. The prose stays in Spanish.


Problem

Decision records in this repository were numbered from a global counter (DEC-0001DEC-0068). A global counter has one authority — "the highest number currently in main" — and that authority does not exist on a branch. Two branches that both add a record both pick the same next number, and the collision is only discovered at merge time, when both records are already written, cross-referenced and cited from code comments.

This is not hypothetical here. It happened twice, and the archaeology is in the repository:

  • 9798390"Renumber serve-time-transform ADR to DEC-0045 (DEC-0043 taken by GeoGebra Fix Geogebra grade item detection #30)". Two branches claimed DEC-0043; one had to be renamed after the fact.
  • research/tareas/diario/2026-06-17-adr-validacion-xapi-y-2.0.yaml records the second: an xAPI record was opened as DEC-0059, discovered to collide with four records on the unmerged feature/secure-iframe-scorm-bridge branch (DEC-0059DEC-0062), and renumbered to DEC-0063 — file, id, and every reference in status.yaml, AN-014, FTE-017 and the journal.

A renumber is expensive because an identifier is not just a filename: it is quoted in PHP comments, in scripts/, in workflow step names, in blueprint.json, and in dozens of research documents. The counter also carries no information — DEC-0045 tells you nothing about which change produced it.

Identification model

Adopted from exelearning/exelearning#2232, keeping this repository's DEC- prefix and its Spanish prose.

A decision is identified by the GitHub tracking number of the change that motivates it: the issue when the change has one, otherwise its pull request. GitHub allocates issue and PR numbers from a single repository-wide sequence — in GitHub's data model a pull request is an issue, which is why /issues/<n> resolves to a PR — so the two can never collide.

DEC-<tracking-number>-<local-sequence>-<slug-de-la-decision>.md
  • <tracking-number> has no leading zeros.
  • <local-sequence> is two digits, scoped only to that tracking number, starting at 01, present even when a change produces a single record — so adding a second one later never renames the first.
  • <slug-de-la-decision> names the decision, not the topic.
  • Frontmatter: id equals DEC-<number>-<NN>, tracking_issue holds the number, legacy_id keeps the retired identifier.
  • The H1 is exactly # <id>: <título>.
  • Status lives in the frontmatter only.
  • No issue was opened to obtain a number — issues are disabled on this repository, so PR numbers are used wherever a change had no pre-existing issue.

There is no global counter and no next-free-number to compute. Two branches can only collide if they share a tracking number, in which case they are the same change.

tracking_issue and legacy_id keep their English names on purpose: they are the fields shared with the other eXeLearning repositories applying this convention. Prose, titles, agentes, fuentes, relacionados and herramienta_ia remain in Spanish and are untouched.

How each number was established

By order of authority, and re-derived independently for every record rather than trusted from an inventory:

  1. An explicit issue reference inside the record itself.
  2. The (#N) subject of the squash commit that added the file (git log --diff-filter=A --follow -- <path>).
  3. The pull request title, when the file arrived by direct push to main and that PR names the record explicitly.

32 of the 46 numbers match the adding commit's (#N) exactly. The 14 that differ all do so for a documented reason: issue #13 (12 records) and issue #29 (1 record) are preferred over the PRs that implemented them, and DEC-16-01 arrived via direct push in 0f65dc5 and is attributed to PR #16, whose title names DEC-0027.

Old → new mapping

46 records migrated, all with git mv.

Retirado Actual Nº de seguimiento
DEC-0016 DEC-4-01 #4 (PR)
DEC-0017 DEC-5-01 #5 (PR)
DEC-0018 DEC-6-01 #6 (PR)
DEC-0020 DEC-11-01 #11 (PR)
DEC-0021 DEC-12-01 #12 (PR)
DEC-0022 DEC-13-01 #13 (issue)
DEC-0023 DEC-13-02 #13 (issue)
DEC-0024 DEC-13-03 #13 (issue)
DEC-0025 DEC-13-04 #13 (issue)
DEC-0026 DEC-13-05 #13 (issue)
DEC-0027 DEC-16-01 #16 (PR)
DEC-0028 DEC-13-06 #13 (issue)
DEC-0029 DEC-13-07 #13 (issue)
DEC-0030 DEC-13-08 #13 (issue)
DEC-0031 DEC-13-09 #13 (issue)
DEC-0032 DEC-17-01 #17 (PR)
DEC-0033 DEC-18-01 #18 (PR)
DEC-0034 DEC-19-01 #19 (PR)
DEC-0035 DEC-19-02 #19 (PR)
DEC-0037 DEC-13-10 #13 (issue)
DEC-0038 DEC-25-01 #25 (PR)
DEC-0039 DEC-26-01 #26 (PR)
DEC-0040 DEC-26-02 #26 (PR)
DEC-0041 DEC-26-03 #26 (PR)
DEC-0042 DEC-13-11 #13 (issue)
DEC-0043 DEC-29-01 #29 (issue)
DEC-0044 DEC-34-01 #34 (PR)
DEC-0045 DEC-34-02 #34 (PR)
DEC-0046 DEC-36-01 #36 (PR)
DEC-0047 DEC-37-01 #37 (PR)
DEC-0048 DEC-66-01 #66 (PR)
DEC-0049 DEC-67-01 #67 (PR)
DEC-0050 DEC-13-12 #13 (issue)
DEC-0051 DEC-68-01 #68 (PR)
DEC-0052 DEC-69-01 #69 (PR)
DEC-0053 DEC-70-01 #70 (PR)
DEC-0054 DEC-71-01 #71 (PR)
DEC-0055 DEC-72-01 #72 (PR)
DEC-0056 DEC-74-01 #74 (PR)
DEC-0057 DEC-77-01 #77 (PR)
DEC-0058 DEC-78-01 #78 (PR)
DEC-0064 DEC-85-01 #85 (PR)
DEC-0065 DEC-106-01 #106 (PR)
DEC-0066 DEC-108-01 #108 (PR)
DEC-0067 DEC-110-01 #110 (PR)
DEC-0068 DEC-111-01 #111 (PR)

Not migrated — 18 records, reported honestly

DEC-0001DEC-0015, DEC-0019, DEC-0036, DEC-0063 keep the retired numbering. All eighteen were pushed straight to main during the repository's bootstrap period: gh api repos/.../commits/<sha>/pulls returns an empty list for every one of their adding commits. They have no verifiable tracking number, and no number was invented and no issue was opened to manufacture one.

Leaving them is safer than a half-guess: those identifiers still resolve, because the files still exist under those names, so every existing reference to them remains correct. The list is frozen in check_decisions.py — any new DEC-NNNN-*.md fails validation, so the debt cannot grow. Their adding commits and three options for closing the gap are recorded in the migration map.

Records and references

  • 64 records total: 46 migrated, 18 on the frozen bootstrap list.
  • 171 files had references updated, 937 reference lines rewritten, across PHP comments, JS, tests, Behat features, docs/, scripts/*.sh, .github/workflows/* step names, blueprint.json, Makefile, AGENTS.md, research/status.yaml, backlog, journal, analysis and source notes.
  • 241 files changed in total, +3017 / −1337, in 5 commits.

Non-mechanical parts were handled by hand: abbreviated group references (DEC-0022/0037) were expanded before rewriting, 9 range references (DEC-0051..0054) were rewritten as prose, and amd/build/fullscreen.min.js + .map were updated in step with amd/src/fullscreen.js. The identifier there lives in a comment, so the sourcemap mappings string is byte-identical before and after (558 chars both sides) — only sourcesContent changes.

Every renamed record additionally gained the mandatory # <id>: <título> H1; the corpus previously had none. No ## Estado sections existed, so that cleanup was a no-op, but the validator now enforces it.

Three one-sided supersession relationships were completed so the graph validates in both directions: DEC-13-04 ↔ DEC-13-05, DEC-19-02 ↔ DEC-25-01, and DEC-13-08 ↔ DEC-111-01. The third was documented, not inferred — DEC-111-01's body already said "Supersede a DEC-0030" and listed it under relacionados.

How the index is generated

The index is derived from the records' own frontmatter and never hand-maintained.

  • make architecture-records prints it on demand (check_decisions.py list).
  • research/docs/indices/adrs.yaml continues to be produced by the repository's existing research/tools/build_indexes.py, alongside the seven sibling indexes that were already generated this way. build_indexes.py now sorts by tracking number and local sequence — plain filename order put DEC-106-01 before DEC-11-01. Re-running it leaves the tree clean.

The curated "Decisiones clave" table in AGENTS.md was kept, not deleted: it carries editorial commentary that exists in no frontmatter field. It is now explicitly labelled a curated summary and points at the generated index as authoritative.

What CI now validates

A new Validate decision records step in the existing matrix-free release-workflow-check job, next to the other static guards. The checker's own unit tests run first, so a broken checker cannot silently pass everything.

research/tools/check_decisions.py validates:

  • filename grammar (and rejects any new file on the retired global numbering);
  • frontmatter: id matches the filename, tracking_issue matches the number, legacy_id present on migrated records, status from a known set, plausible dates, herramienta_ia provenance present;
  • the H1 mirrors id and titulo, and no duplicated ## Estado section exists;
  • duplicate ids and duplicate local sequences;
  • the supersession graph in both directions, rejecting one-sided links, self-supersession and unknown targets;
  • research/decisiones/cambios/<n>-<slug>/ design directories, when any exist;
  • retired identifiers tree-wide, outside the migration map, the legacy_id field and a documented allowlist of historical journal entries.

Tooling is Python 3, stdlib only, not Bun/TypeScript: this repository has no Node/Bun runtime of its own (bun is only fetched to build the embedded editor) and research/tools/ already ships Python tools, so no new runtime is introduced. research/tools/test_check_decisions.py carries 42 unit tests, three of which assert that the real corpus passes.

research/schemas/decision.schema.yaml, research/decisiones/README.md, research/plantillas/markdown/plantilla-adr.md, research/README.md, research/AGENTS.md and research/tools/README.md were updated for the new policy.

Commands run, with results

make architecture-check                              exit=0   Decision records OK — 64 records
                                                              (18 still on the retired numbering),
                                                              0 change directories.
                                                              Ran 42 tests in 0.041s — OK
make architecture-records                            exit=0   prints the 64-record index
make lint            (composer lint / phpcs moodle)  exit=0   clean
make test-js         (vitest)                        exit=0   2 files passed, 37 tests passed
python3 research/tools/build_indexes.py              exit=0   idempotent (git diff empty afterwards)
bash scripts/check-release-workflow.sh               exit=0
bash scripts/check-version-selftest.sh               exit=0   enforces the DEC-111-01 version policy
bash scripts/check-version.sh                        exit=0
bash scripts/check-package.sh                        exit=0
php -l on all 83 changed PHP files                            0 failures

Independent verification performed against the branch, not against the diff's own claims:

  • All 46 renames are real renames: git diff --find-renames main..HEAD reports 46 R entries and zero add/delete pairs. git log --follow traverses each one, including through the pre-existing DEC-0043 → DEC-0045 rename in 9798390.
  • Frontmatter was diffed field by field, old file on main against new file on the branch, for all 46. No field was dropped. agentes, fuentes, fecha, titulo and herramienta_ia are byte-identical in every record; the only changes are id, the added tracking_issue / legacy_id, updated relacionados, and the three supersession completions above. No estado value changed.
  • Every filename, id, tracking_issue and H1 was re-derived and cross-checked: 0 mismatches, 0 duplicate ids, 0 duplicate local sequences, every sequence starts at 01 and is contiguous, no leading zeros.
  • git grep for all 46 retired identifiers across the whole tree returns hits only in the migration map, legacy_id fields, the validator's own test fixtures, and one historical journal entry that is explicitly annotated as a historical record.
  • All relative markdown links and [[wikilinks]] across research/ and docs/ resolve: 0 broken.
  • Negative tests against the real tree, both reverted: an untracked file containing DEC-0030exit=1; a new DEC-0099-*.mdexit=1; a duplicated DEC-13-01exit=1. Clean tree → exit=0.

Not run

make test (PHPUnit) and make behat require bringing up the Docker Moodle stack, which binds host port 80 and initialises Moodle and a PHPUnit database on first run. The PHP diff is comment-only except two diagnostic strings — a debugging(..., DEBUG_DEVELOPER) message in classes/local/track.php and a PHPUnit assertion message in tests/lib_test.php. All 18 assertDebuggingCalled() call sites in the suite are argument-less, so no test asserts either string.

Known limitations

  1. The 18 bootstrap records stay on the retired numbering, as described above. Options: leave them, group them under this PR's number, or have a maintainer supply numbers. Needs a ruling.
  2. No local adoption record is included. Writing one requires this PR's own number, which does not exist until the PR is opened. It can be added as DEC-<pr>-01-… in a follow-up commit on this branch. Meanwhile the policy README cites exelearning/exelearning#2232 as the source of the convention.
  3. Local numbers 13 and 29 are used, not the transferred upstream numbers. Both issues were later transferred to exelearning/exelearning (#2058 and #2056). The local numbers are used because they remain permanently consumed in this repository's issue/PR sequence, https://github.com/exelearning/moodle-mod_exelearning/issues/13 still returns a 302 to the new location (verified), and every existing document already says "issue #13". Adopting 2058 would import another repository's numbering and would eventually collide with this repository's own PR #2058. The rationale is recorded in the migration map.
  4. Issues are disabled on this repository, so in practice new records here will almost always be identified by their pull request. The convention is unchanged — prefer the issue when one exists — but the issue branch will rarely apply locally.
  5. PR Secure opaque-origin viewer, SCORM/xAPI bridge and opaque editor preview #80 (feature/secure-iframe-scorm-bridge) adds 7 further records on the retired scheme. It is deliberately untouched here and must migrate them on its own branch; the validator will fail it until it does.
  6. research/decisiones/cambios/ does not exist yet. The validator supports change directories, but no design document in this repository currently warrants one, and empty scaffolding was not created.

Moodle Playground Preview

The changes in this pull request can be previewed and tested using a Moodle Playground instance.

Preview in Moodle Playground

ℹ️ The eXeLearning editor is fetched from the shared release and unpacked into the plugin when the playground boots, so the first load may take a few extra seconds. ELPX upload, viewer and preview work normally.

erseco added 5 commits August 5, 2026 09:36
Replace the global DEC-NNNN counter with the tracking-number scheme adopted in
exelearning/exelearning#2232: a decision is identified by the GitHub number of
the change that produced it -- the issue when there is one, otherwise the pull
request -- plus a two-digit sequence scoped to that number
(DEC-<tracking-number>-<NN>-<decision-slug>.md).

GitHub allocates issue and pull-request numbers from a single repository-wide
sequence, so two branches can only collide when they share a tracking number.
The retired counter could not offer that: this repository has already had to
renumber records twice (DEC-0043 -> DEC-0045 in PR #35, and DEC-0065..0067 ->
DEC-0069..0071 on the open secure-iframe branch).

This commit only lands the policy: the decisiones README, the ADR template, the
frontmatter schema and the research meta-docs. The records themselves are
migrated next.

The prefix stays DEC-, the prose stays Spanish and the frontmatter keeps its
Spanish field names. Only `tracking_issue` and `legacy_id` are English, because
they are the fields shared with the other eXeLearning repositories that follow
this convention.
Rename every decision record whose GitHub tracking number could be established
from the repository's own history, and rewrite the cross-references between
records.

The tracking number was resolved, in order of authority, from an explicit issue
reference in the record itself, the `(#N)` subject of the squash commit that
added the file (`git log --diff-filter=A --follow`), and the pull request title
when the file landed as a direct push. Twelve records belong to umbrella issue
#13 and one to issue #29; the other thirty-three take the number of the pull
request that delivered them.

Each migrated record gains `tracking_issue` and `legacy_id`, and every record --
migrated or not -- gains the mandatory `# <id>: <titulo>` H1, which the corpus
had never carried. Slugs are preserved verbatim: the decision each one names is
unchanged, and keeping them makes the id substitution also fix every filename
reference. All Spanish prose, `agentes`, `fuentes`, `relacionados` and
`herramienta_ia` provenance are untouched.

Three one-sided supersession relationships are completed, as the policy now
rejects them:

  - DEC-13-04 (ex DEC-0025) <-> DEC-13-05 (ex DEC-0026)
  - DEC-19-02 (ex DEC-0035) <-> DEC-25-01 (ex DEC-0038)
  - DEC-13-08 (ex DEC-0030) <-> DEC-111-01 (ex DEC-0068)

The first two counterparts already declared `supersede`. For the third, the
superseding record's own body states "Supersede a DEC-0030" and lists it under
`relacionados`, so the relationship is documented, not inferred.

Eighteen records keep the retired numbering: DEC-0001..DEC-0015, DEC-0019,
DEC-0036 and DEC-0063 were pushed straight to `main` during the bootstrap
period, reference no issue and no pull request, and have no verifiable tracking
number. They are not renamed and no number is invented for them.

Renames use git mv, so `git log --follow` still resolves the full history.
Rewrite the 2,038 references to the 46 migrated records across the repository:
PHP source comments, JavaScript, tests and Behat features, the docs/ suite, the
release scripts, the CI workflow step names, blueprint.json, AGENTS.md and the
research corpus (status.yaml, backlog, journal, analysis and source notes).

Because every slug is preserved, a reference to the record's filename
(`DEC-0030-version-sentinela-en-main.md`) is fixed by the same substitution as
the bare identifier.

Notes on the non-mechanical parts:

  - Abbreviated group references (`DEC-0022/0037`) were first expanded to full
    identifiers, since the shorthand cannot survive a numbering change.
  - Range references (`DEC-0051..0054`) were rewritten by hand: a contiguous
    range has no meaning once identifiers are tracking numbers.
  - amd/build/fullscreen.min.js and its source map carry the comment verbatim
    from amd/src/fullscreen.js. They are updated in step, which is byte-for-byte
    what grunt regenerates: the identifier lives in a comment, so no mapping
    segment moves.
  - The AGENTS.md decision table is a curated editorial summary, not an index;
    it keeps its commentary and now says so, and points at the generated index
    as authoritative.
  - The 2026-06-17 journal entry that records the DEC-0059/DEC-0063 collision
    keeps its retired identifiers: it is the historical evidence for retiring
    the global counter.

References to the eighteen records that keep the retired numbering are
unchanged, because those identifiers still resolve.
…index

The map records every retired identifier, its current one, the tracking number
and how that number was established, so a reference in an old PR, commit message
or notebook can still be resolved.

It also states plainly what was not migrated: the eighteen bootstrap records
that have no verifiable tracking number, the evidence for that (their adding
commits), and the options for closing the gap. No number was invented and no
issue was opened to obtain one.

build_indexes.py now sorts the decision index by tracking number and local
sequence. Filename order would put DEC-106-01 before DEC-11-01 and DEC-4-01
after DEC-37-01, which is unreadable for an index whose whole job is to be
scanned by a human.
Add research/tools/check_decisions.py, a stdlib-only Python 3 validator that
matches the existing research/tools/ toolchain. The reference implementation in
exelearning/exelearning is Bun + TypeScript; this repository has no Node/Bun
runtime of its own (bun is only fetched to build the embedded editor) and its
record corpus already ships two Python tools, so porting is the right call
rather than adding a runtime for one script.

It validates:
  - the filename grammar DEC-<tracking-number>-<NN>-<decision-slug>.md, and
    rejects any NEW record on the retired numbering. The eighteen bootstrap
    records are a frozen allowlist, so the debt cannot grow;
  - frontmatter: id matches the filename, tracking_issue matches the number,
    valid status and date, agentes, and herramienta_ia provenance;
  - the mandatory `# <id>: <titulo>` H1, and the absence of an `## Estado`
    section duplicating the frontmatter;
  - duplicate ids and duplicate local sequences;
  - the supersession graph in both directions, including the superseded
    record's status;
  - design-document directories under research/decisiones/cambios/, when they
    exist;
  - retired identifiers anywhere in the tree, outside a documented allowlist.
    Bootstrap identifiers are deliberately not flagged: those records still
    exist under that name, so a reference to them resolves.

`list` prints the index from the frontmatter -- the index is derived, never
hand-maintained.

Wired into make (architecture-records / architecture-check) and into the CI job
release-workflow-check, which is the existing home for static guards: no matrix,
no Moodle bootstrap, and python3 is already present on the runner.

42 unit tests cover the checker, including three that assert the real corpus
satisfies its own rules.
@codecov-commenter

codecov-commenter commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.31%. Comparing base (0be72e3) to head (16ad1e2).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #114   +/-   ##
=========================================
  Coverage     92.31%   92.31%           
  Complexity      824      824           
=========================================
  Files            51       51           
  Lines          3734     3734           
=========================================
  Hits           3447     3447           
  Misses          287      287           
Flag Coverage Δ
javascript 94.11% <ø> (ø)
php 92.23% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
PHP (server-side) 92.23% <100.00%> (ø)
JavaScript (SCORM tracker) 94.11% <ø> (ø)
Files with missing lines Coverage Δ
classes/completion/custom_completion.php 93.75% <ø> (ø)
classes/event/activity_migrated.php 95.23% <ø> (ø)
classes/event/activity_skipped.php 93.75% <ø> (ø)
classes/event/attempt_completed.php 81.81% <ø> (ø)
classes/event/migration_failed.php 93.33% <ø> (ø)
classes/event/migration_started.php 100.00% <ø> (ø)
classes/external/save_track.php 100.00% <ø> (ø)
classes/grades/completion_validator.php 95.00% <ø> (ø)
classes/grades/grade_item_manager.php 98.82% <ø> (ø)
classes/grades/grade_recalculator.php 98.07% <ø> (ø)
... and 27 more
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

erseco added 5 commits August 5, 2026 11:43
…NNN ids

Replaces `check_decisions.py` with the shared `architecture_records.py`,
byte-identical to the copy in the other three repositories. Only the paths
and the `DEC` prefix are configured; every rule is the same everywhere.

The 18 bootstrap records that had no verifiable tracking number are now
`DEC-0-01` … `DEC-0-18`. `0` is a sentinel for records that predate
tracking: GitHub numbers issues and pull requests from 1, so it can never
collide with a real one, and unlike `1` it does not claim a pull request
that exists and is about something else. `check` no longer reports records
on the retired numbering — there are none.

Frontmatter keys are translated to English so the schema matches the other
repositories exactly (`titulo` -> `title`, `estado` -> `status`,
`relacionados` -> `related.adrs`, `herramienta_ia` -> `ai_assistance`, …),
and status values with them. **The prose stays in Spanish**: this change is
about the schema, not the language.

`relacionados` mixed record ids with references to other research artifacts
(AN-, RIE-, REPO-). Record ids became `related.adrs`, which the validator
now checks; the rest moved to `see_also`, which it leaves alone.

  make architecture-check   OK — 64 records (18 from repository bootstrap)
  unittest                  OK — 48 tests
…on port

The validator is now one TypeScript file, byte-identical in every
repository. It runs under Bun in core, where `bun test` covers it, and
under the Node that ships on the CI image in the plugins, which need no
setup step and no dependency.

This replaces the Python port from the previous commit. Python was chosen
when the alternative looked like installing Bun in four PHP pipelines. It
is not: `setup-bun` appears only in release workflows that never run on a
pull request, but Node is preinstalled on the runner image, and one source
file runs under both runtimes once it avoids Bun-specific APIs.

That keeps the tests where the scaffolding already is. Core keeps its 47
`bun test` cases with `expect()`; the plugins run the file itself on every
pull request, which is what would surface a Node-specific regression.

Repository-specific values live in `architecture-records.json` — paths and
the record prefix only. Every rule is identical everywhere.

  bun run scripts/architecture-records.mts check   OK
  node scripts/architecture-records.mts check      OK (same output)
…sing

Making `deciders` optional was the wrong reconciliation. It weakened the
rule in every repository to accommodate five records that simply had not
recorded who decided — a gap to fill, not a rule to relax.

The field is required again, and the five records in wp-exelearning and
omeka-s-exelearning now carry `@erseco` and `claude-code`, matching the
pattern moodle-mod_exelearning already used for the same work.

No model had to be inferred: `ai_assistance` was already present and
consistent with each record's date (claude-fable-5 in July, claude-opus-5
in August).

  wp, omeka, nextcloud, moodle, core   architecture-check OK
  core                                 bun test 47 pass
Four files still named `check_decisions.py` and its test module, both
removed when the validator became a shared TypeScript file copied from
core. Also records that the copy here must not be edited locally.

Refs exelearning/exelearning#2232
Five documents still pointed at `scripts/architecture-records.ts`, which no
longer exists: the file became `.mts` so the same source runs under Bun in
core and under Node in the plugin repositories.

The script's own usage header said `bun run` unconditionally, which is wrong
in the four repositories that carry a copy and run it with Node. It now
states both, and why the file must avoid runtime-specific APIs.

  core   architecture-check OK, bun test 47 pass, lint OK
  all 5  byte-identical copies, architecture-check OK
@erseco
erseco merged commit 8fb8f0e into main Aug 5, 2026
25 checks passed
@erseco
erseco deleted the docs/architecture-tracking-number-identifiers branch August 5, 2026 10:51
erseco added a commit that referenced this pull request Aug 5, 2026
main adopted tracking-number decision identifiers (#114) while this branch was
open, so the merge collides on every file that names a record.

Seven conflicts, resolved as follows.

view.php, comment block above $emitsxapi: kept this branch's longer comment,
which documents the channel choice for BOTH iframe modes; main's shorter version
only knows the same-origin one.

view.php, xapi_config() argument list: kept this branch's $hostorigin. The two
sides close different calls. Main closes scorm_config(...) with its $emitsxapi
argument and then adds the SCORM tracker boot and an xAPI listener block; this
branch already carries all of that, restructured and moved earlier, and its own
xAPI listener additionally handles opaque-origin mode by trusting event.source
instead of event.origin. Verified after resolving: scorm_config() is still called
with $emitsxapi, and the file has exactly one xAPI listener block.

settings.php: kept this branch's cspprofile and embedmode settings — they are the
feature — on top of main's editor-toggle comment.

DEC-36-01: main renamed the file, this branch edited it. Applied this branch's
superseded-in-part note and its extra related entry on top of main's renamed file
and new frontmatter.

research/status.yaml: RIE-001 keeps main's dated acceptance note and takes this
branch's estado (mitigado, since this branch is the mitigation); its relacionados
list is the union of both sides.

docs/xapi-qa-checklist.md, AN-008: kept this branch's text, which is a superset of
main's.

research/docs/indices/adrs.yaml is generated; regenerated in a later commit.
erseco added a commit that referenced this pull request Aug 5, 2026
main retired the global DEC counter in #114: a record is now named after the
GitHub number of the change that motivates it — the issue when there is one,
otherwise the pull request. Issues are disabled here, so it is the PR: 80.

The seven records this branch adds become DEC-80-01 … DEC-80-07, renamed with
git mv so their history follows. Their frontmatter keys move to English to match
what main now validates (the prose stays in Spanish), each gains tracking_issue
and legacy_id, and the flat `relacionados` list splits into `related.adrs` for
records and `see_also` for the other artefacts. The pre-migration corpus had no
H1; each record gains the required `# <id>: <title>` heading.

References are remapped repo-wide from the tree itself — every migrated record
carries its legacy_id, so the retired-to-current mapping is derived, not typed.
mapa-migracion-ids.md gains the seven rows and its "reserved in open branches"
note becomes the record of what those identifiers ended up as.

Three references to eXeLearning core ADRs were remapped the same way, reading the
new identifiers out of core's own frontmatter on feature/preview-trust-boundary:
ADR-0017 -> ADR-2199-08, ADR-0018 -> ADR-2199-09, ADR-0021 -> ADR-2199-12. That
also makes the vendored js/exe_external_media/verify.mjs byte-identical to the
copy core publishes again, which is the point of vendoring it.

The two "ADR-0026/0027" mentions were never local identifiers: they name
Procomún's own decision documents, in a repository that keeps its own numbering.
They now cite those documents by path, so they no longer read as retired eXe
identifiers.
erseco added a commit that referenced this pull request Aug 5, 2026
#114 moved the ADR frontmatter keys to English but left build_indexes.py reading
`titulo`/`estado`/`fecha`, so the generator and the committed index no longer
agreed: running it on main rewrote all 64 ADR entries to `titulo: '<filename>',
estado: None, fecha: None`. The index survived only because nobody re-ran it.

index_md() now reads the English key first and falls back to the Spanish one, so
the same generator serves the migrated ADRs and the notes and sources that still
use Spanish frontmatter.

Regenerating with that fix changes adrs.yaml in exactly two ways: the seven
DEC-80-* records appear, and 60 entries pick up the `estado` their frontmatter
actually carries (Aceptada -> Accepted, Propuesta -> Proposed). No entry loses a
title, a state or a date, and the other seven indexes come out unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants