Skip to content

fix(lvs): freeze a verified LVS score now that LvsResult is no longer append-only - #1195

Merged
rubenvdlinde merged 3 commits into
developmentfrom
fix/lvs-score-freeze
Sep 28, 2026
Merged

rubenvdlinde merged 3 commits into
developmentfrom
fix/lvs-score-freeze

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Scope

Since #1124 took appendOnly off LvsResult so verify and archive could run, coordinators and compliance officers could also edit a verified LVS score. A new LvsResultFreezeListener fixes the score fields once a result is verified and lets a verified result only move on to archived, in the style of AssessmentResultIntegrityListener.

  • Follow-up named in openspec/changes/access-control-ratchet-compliance/design.md ("a freeze listener in the style of AssessmentResultIntegrityListener is a named follow-up"); TRACKER-R2 "ROUND 3 FINAL": LVS score freeze listener.
  • Frozen once verified or archived: provider, instrument, moment, takenAt, rawScore, vaardigheidsscore, niveau, referentieniveau, dle, dataExchangeJobId, tenant_id. learnerId (learner merge) and assessmentResultId (back-link) stay writable; an imported row can still be corrected.
  • Admins and system context (the import job) are not policed. Delete is unchanged: the schema grants none, so only admins delete.
  • The AssessmentResult integrity wiring and the new freeze move into EvidenceFreezeListenerRegistrar, called from IntegrityListenerRegistrar, which had hit phpmd's coupling limit.
  • Register: LvsResult 0.2.1 to 0.2.2 (description no longer says append-only; dataExchangeJobId nullable), info.version 0.30.0 to 0.30.1.

Change: openspec/changes/lvs-score-freeze/.

Verification

  • vendor/bin/phpunit --filter LvsResultFreezeListenerTest red first (8 errors, 2 failures with the listener absent and unwired), then green (10 tests).
  • composer check:strict (TMPDIR in the clone, timeout off): first run exit 1 on phpmd only (IntegrityListenerRegistrar coupling 13, my line); lint, phpcs, psalm, phpstan clean and test:all 2121 tests OK (5 skipped). After the registrar split: composer phpmd 0, composer psalm 0, composer phpstan 0.
  • npm run lint 0 (2 inherited warnings), npm run format 0, npm run check:schema-l10n 0. No test:l10n script.
  • openspec validate lvs-score-freeze valid.
  • Hydra gates (vendor/conduction/hydra-gates/hydra-gates/scripts/run-hydra-gates.sh, HYDRA_GATE_BASE_REF=origin/development): exit 6. Six whole-tree gates red, none on this diff: 3 stub-scan (caller-identity-ignored in six untouched services), 25 contract-coverage, 49 controller-exception-translation, 55 detail-page-discipline, 60 icon-vocabulary, and 101 demo-data-coverage. Gate 101 also named LvsResult (demo row with dataExchangeJobId: null against a non-nullable string, from feat(data-exchange): integriq carries the exchanges, learniq keeps the gates (D7, D25) #1157); fixed here by declaring it nullable, as its description already says. Re-run standalone (generate_mock_register.py . --check --only-changed): LvsResult passes; DossierReview, ExchangePartnerApproval, TeldatumCheck (no demo rows) and OsoImportDossier (same null) remain, inherited.

Inherited

phpcs warnings on untouched files (missing @spec on other classes) and two eslint warnings; none on lines this PR touches.

Landing note

Touches lib/Settings/learniq_register.json (info.version, LvsResult); other f-quality PRs bump info.version too.

🤖 Generated with Claude Code

… append-only

Since #1124 dropped appendOnly so verify and archive could run, coordinators
and compliance officers could also edit a verified score. LvsResultFreezeListener
fixes the score fields once a result is verified, lets a verified result only
move on to archived, and leaves admins and the import job unpoliced. The
AssessmentResult and LvsResult freezes move into EvidenceFreezeListenerRegistrar
to keep IntegrityListenerRegistrar under the coupling limit.
See openspec/changes/lvs-score-freeze/.
…ption and demo row say

A result entered by hand has no exchange job; gate 101 refused the demo row.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/learniq @ df988f4

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-specs ✅
format ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 107/107
npm ✅ ✅ 747/747
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it on any branch from the Actions tab, or locally with npx playwright test.
Hydra gates ❌

Quality workflow — 2026-09-28 16:13 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 837b9be into development Sep 28, 2026
38 of 40 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/learniq @ b28c234

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-specs ✅
format ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 107/107
npm ✅ ✅ 747/747
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it on any branch from the Actions tab, or locally with npx playwright test.
Hydra gates ❌

Quality workflow — 2026-09-28 16:38 UTC

Download the full PDF report from the workflow artifacts.

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.

1 participant