Repository navigation
fix(review): #4580 review findings, round 1 - #4655
Open
WilcoLouwerse wants to merge 33 commits into
Open
WilcoLouwerse wants to merge 33 commits into
WilcoLouwerse wants to merge 33 commits into
Conversation
…isfied by an upload token
validateAll() now excuses a property that carries an upload in the plan's files ('uploaded'), because mapPayload() moves the file out of the validated object. A required file property with a valid token is accepted; without a token it still answers 422 required.
Thread: #4580 (comment)
Sweep: saveDraft/promoteDraft/submit all go through validateAll, so one place. DraftActivation does not see form uploads.
Check: phpunit tests/Unit/Service/Form/FormSubmitServiceTest.php -> OK 26 tests (new test errors without the fix); phpcs on the lib file clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hen it cannot be read An unreadable schema now yields a decider-owned policy that also hides others, and a throwing file-to-object lookup in subjectOf() yields the same closed policy instead of the open instance default (also covers the surface slice's duplicate of this finding). Thread: #4580 (comment) Sweep: both failure branches of ReviewPolicyResolver now share closedPolicy(); no other resolver builds a fail-closed review policy. Check: phpunit tests/Unit/Service/Decision -> OK 17 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
applyBulkSafeguards() now asks DraftStatusPolicy::keptFrom() for an existing row when RBAC is enforced and rejects the row with the same 'Permission denied' reason a refused update gives, so a draft stays unannounced. The owner's bulk save still updates it. Tests in Wave12BulkSafeguardsTest (the test fails without the fix). Thread: #4580 (comment) Sweep: keptFrom had one caller (ObjectService::refuseSomebodyElsesDraft); the bulk safeguard is the only other path that resolves an existing row past RBAC. Check: phpunit Wave12BulkSafeguardsTest + BulkSafeguardSchemaResolutionTest OK; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rough a share A node outside the openregister home (a sharee's view: /alice/files/<share>/x.pdf) is now resolved by file id: only a node owned by the app account is looked up, in the app's home via IRootFolder::getById, and the existing UUID-ancestor + folder proof runs on that twin. A node that does not exist yet stands for its nearest existing ancestor. A home that cannot be searched fails closed with a log line. New constructor argument: IRootFolder (autowired). Thread: #4580 (comment) Sweep: the listener is the only path-prefix owner test (grep for '/files/' prefix checks in lib/ finds no other frozen-file gate). Check: phpunit tests/Unit/Listener/FrozenNodeWriteListenerTest.php OK 21 tests; phpcs on the lib file clean. The Newman PUT-as-sharee case from Done-when was not run (no live Nextcloud). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he instance review default updateFileSettingsOnly() writes anonymisationReview only when the request carries the key; without it the stored declaration is kept, and an explicit null still clears it. The test that asserted null after an empty save now asserts the kept value, plus the explicit-null clear. The Vue form is left alone: the server keeps the stored value, so the form need not send it. Thread: #4580 (comment) Sweep: ReviewPolicy::SETTINGS_KEY is written nowhere else (ReviewPolicyResolver only reads it). Check: phpunit tests/Unit/Service/Settings/FileSettingsHandlerTest.php OK 20 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and 33 accept 4000 The chunk budget now follows the running Nextcloud: ListenerDeferralService::getArgumentLimit() reads OC\BackgroundJob\JobList::MAX_ARGUMENT_JSON_LENGTH when the server defines it (34 and later: 32000) and uses 4000 otherwise, which is what NC 32 and 33 enforce. Picking by constant rather than a fixed 4000 keeps 35 at full chunks; the 4000 fallback is the safe side everywhere. The limit can be injected through the constructor (optional, autowired as null). An entry that alone exceeds the limit is enqueued without its oldData and the reduction logged at warning level (the 'updated' notification is still sent, only calculatedChange is skipped); an entry still too large is dropped and named as before. MAX_ARGUMENT_LENGTH is replaced by ARGUMENT_LENGTH_NC32/NC34 (no other user). DeferredChunksFitTheJobLimitTest runs at 4000 and at 32000 and covers the reduction; the 'too large for any job' test now uses an entry whose size is not oldData. Thread: #4580 (comment) Sweep: the openspec change deferred-listener-batches-stay-under-the-job-limit (proposal + spec requirement) restated 32,000 and is updated to the runtime limit and the reduction. Check: phpunit tests/Unit/Service/Deferral + ObjectCleanupJobTest OK 34 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FormsController::upload() applies submit()'s audience rule (shared audienceRefuses()/signInRequired() helpers): an anonymous visitor of a form that is not public gets 401 before any rule lookup or write. FormUploadStore also gets a 20 MiB default cap for a file property that declares no maxSize (surface slice's duplicate of this finding); a declared limit, larger or smaller, is kept. Thread: #4580 (comment) Sweep: FormUploadStore::issue has one caller (FormsController::upload). Check: phpunit FormsControllerTest + FormUploadStoreTest OK 27 tests; phpcs on the lib files clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nor race-safe The stored answer is now bound to the submitter (user id, scope value or anonymous marker) and a canonical hash of the payload: another subject, or the same key with another payload, gets a 409 that carries nothing of the first answer. A key is reserved with an exclusive ILockingProvider lock for the length of its submit (acquire, find, write, remember, release in a finally), so a parallel retry answers 409 and creates no second object; the lock is released after a refusal too. FormIdempotencyStore gets ILockingProvider and IL10N (autowired), find()/remember() take the binding, and an entry without a binding (older than this change, at most 24 h) matches nobody. Tests use a small in-memory MemoryLockingProvider. docs/features/form-destination.md gets the 409 row and the binding sentence (same sentence as the finding's). Thread: #4580 (comment) Sweep: FormIdempotencyStore has one caller (FormSubmitService::submitAll) plus the purge job, which is unchanged; the idempotency header is read only in FormsController::submit. Check: phpunit tests/Unit/Service/Form + FormUploadPurgeJobTest + FormsControllerTest OK 106 tests; phpcs on the two lib files clean. The two-concurrent-requests Done-when is covered through a simulated parallel request inside the save, not a real parallel run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the draft remove() now reads the object again (ObjectService::find, RBAC off, no render, no audit read) and deletes only while it is still a draft last changed before the cutoff the list was made with; a draft sent or touched since is counted as refused and logged at info level, an object that cannot be read again is left alone. The cutoff is passed through walk() and removeWithin(). DraftExpiryTest gets a find() fake and a test with a draft sent after the list. Thread: #4580 (comment) Sweep: DraftExpiry::remove has one caller; DraftExpiryJob and DraftsExpiredCommand only call sweep()/preview(). DraftExpiryJobTest gets the same find() fake (it builds DraftExpiry itself). Check: phpunit tests/Unit/BackgroundJob + DraftsExpiredCommandTest + DraftExpiryTest OK 338 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nothing was unpublished
unpublish() answers 'unpublish-unavailable' (and logs a warning) for a FormDependent registered without a closure, instead of falling through to notify the author and answer 'unpublished'. The outcome is already listed per form in the schema save response ('affectedForms'), so the save surfaces it. FormDependentsCheckTest covers the null closure.
Thread: #4580 (comment)
Sweep: FormDependent::$unpublish is read only in FormDependentsCheck::unpublish.
Check: phpunit tests/Unit/Service/Form/FormDependentsCheckTest.php OK 5 tests; phpcs on the lib file clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ma write path only (logging part) formDependentsCheck() now logs at warning level when the container cannot build the check (it used to return null in silence, which skipped an owning app's refuse setting without a trace), and the lookup happens only for an update that carries properties or required. Test: testAFailingDependentFormCheckIsLogged. Not done here: running the check on the other schema write paths (configuration importer, SchemaImportController, SchemaTool, SchemasToolProvider, SchemaService), because what 'refuse' means for a write that has no response to refuse is the author's call. See the hand-back note. Thread: #4580 (comment) Check: phpunit tests/Unit/Controller/SchemasControllerTest.php OK 83 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion the file lacks Under an administrator's decision only the authorization roots the incoming definition carries are decided; a block only the instance holds keeps its normal state (local is preserved and reported) instead of settling as UPSTREAM and being dropped, which left the schema default-open. New GuardedDescriptorMergeTest case under a ShippedAuthorizationDecision; the replace-when-shipped-has-it cases stay green (the test fails with the old roots($live, $incoming)). Thread: #4580 (comment) Sweep: AuthorizationBlocks::roots() has this one caller. Check: phpunit tests/Unit/Service/ShippedBaseline + RegisterDescriptorServiceTest OK 63 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tatus MagicRbacHandler now takes the MagicMetadataColumnHealer (built first in MagicMapper::initializeHandlers) and an optional bare table name; reachableRowSqlFor() leaves the (_status IS NULL OR _status <> 'draft') term out when the table is known to lack _status, so a non-admin on a degraded table no longer fails with an unknown-column error. The table name is threaded through applyRbacFilters, buildRbacConditionsSql, buildRbacRowPredicateSql, buildRbacPredicateForAlias and buildRbacSqlTerms (all optional; a caller that names no table keeps the predicate, which is the narrower and safe answer) and passed by every caller that knows it: MagicSearchHandler (list, UNION conditions, single-object reads), the three MagicMapper single-object and scan sites, RelatedRowQueryApplier (the related table) and AggregationRunner (via MagicMapper::rbacRowPredicateSql(register:)). New test: testADegradedTableGetsNoDraftPredicate (missing: no _status, owner admit kept; healthy table and unnamed table: predicate kept). Thread: #4580 (comment) Check: phpunit tests/Unit/Db tests/Unit/Service/Aggregation tests/Unit/Service/Query: 2407 tests, 49 errors, all 'PDOException: could not find driver' or 'OC\DB\Schema\Table is not loadable' (no database driver / Nextcloud server classes in this environment), none from the touched code; MagicRbacDraftVisibilityTest OK; phpcs on the touched lib files: no errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…list hides The native aggregation path adds DraftListLens's clause for the mode a query-less list uses (EXCLUDE, or NONE for a schema with draftsListed: false) next to the archive predicate: others' drafts are out of the count, the signed-in caller's own stay in through a bound owner placeholder (kept in step with the organisation bindings that follow), and the clause is left out of a table known to lack _status. This also holds for an administrator, whose RBAC predicate is null. New tests assert the generated WHERE and bindings for nobody, a user, draftsListed false and a degraded table. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/Aggregation: 276 tests, 21 errors ('PDOException: could not find driver', identical at HEAD) and no other failure; phpcs on the lib file clean. The 'count over a table with one draft and one active row returns 1' Done-when needs a database and was not run (SQL-level assertions stand in for it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…earlier refusal DecisionChangeRequestedEvent keeps a refused flag: once refuse() was called, allow() does nothing, so the registration order of two apps' listeners cannot turn a veto into an allow. testAnAllowClearsTheReason is replaced by a sticky-refusal test covering both orders; the supervisor-may-be-refused and listener-may-allow cases are untouched and green. Thread: #4580 (comment) Sweep: DecisionVisibility's event is deny-by-default and allow-only (order independent); no other vetoable event in lib/Event has an allow() that clears a refusal. Check: phpunit tests/Unit/Service/Decision + tests/Unit/Event OK 245 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… remote principals GrantManageGuard::letsItselfIn() takes the remote principal being named and refuses it outright for a holder admitted by the special right alone (a federated cloud id or remote group cannot be proved not to be the caller); ObjectSharingService passes remote and remote_group grantees. The selfGrants data provider of GrantManageSpecialRightTest gains a remote and a remote_group case. The owner and the administrator are unaffected. Thread: #4580 (comment) Sweep: letsItselfIn has two callers; ObjectOwnershipService names only a group (remote is not applicable there). Check: phpunit tests/Unit/Service/Rbac/GrantManageSpecialRightTest.php OK 17 tests; phpcs on the two lib files clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on unguarded When the guard throws, the import still finishes but now writes the incoming definition with the instance's own authorization blocks put back (the schema's, and each property the instance already holds; a block the instance lacks is not added, a property the instance does not hold brings its shipped block), and logs it. Authorization blocks the merge took from the app because only the app changed them are now recorded on the audit trail (configuration.conflict.decided with decision applied-shipped, once per block root, skipping roots already recorded for an explicit decision); recordDecision() gets a decision argument for it. New tests: a throwing baseline read keeps the instance authorization, and an app-only authorization change is audited. Thread: verdict #4580 (review) (this finding has no inline thread) Check: phpunit tests/Unit/Service/ShippedBaseline + RegisterDescriptorServiceTest OK; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ersonal settings form The relink notification now links to the OpenRegister app page with #user-settings (as the OAuth callback does) and says 'user settings'; the admin hint in PushNotificationsConfiguration.vue, its en/nl l10n keys (nl: gebruikersinstellingen), the credential-oauth2-connect requirement heading and text, the notificatie-engine requirement sentence, and the stale header comments in BrowserNotificationsSection.vue and OAuth2Endpoints.php are reworded to the user settings dialog. Other languages' l10n entries keep the old key until Transifex syncs. New NotifierTest case asserts the link. Thread: #4580 (comment) Sweep: git grep -i 'personal settings' over lib, src and openspec/specs leaves only comments that say OpenRegister is NOT in Nextcloud's personal settings (CredentialOauth2Controller, App.vue, UserSettings.vue). Check: phpunit tests/Unit/Notification/NotifierTest.php OK 22 tests; l10n/en.json and nl.json parse. The anchored code is spec text and strings only, no eslint run was needed beyond the one-line template change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ory redaction ObjectsController::logs() answers a non-admin through ObjectHistoryForReaders::page() (the same read check, doelbinding guard and redaction as the audit-trails route: 404 for an object the caller may not read, no session, request or ipAddress, no property the caller may not see); an administrator keeps the stored history as before. The page keeps the results/total/page/pages/limit/offset shape; the legacy next/prev links are not produced on the reader path. The requirement in audit-trail-immutable/spec.md now names both URLs. The 17 existing testLogs* cases of ObjectsControllerTest, which exercise the stored-history path, now run as an administrator. New ObjectsControllerLogsRedactionTest covers delegation, the 404 and the administrator path; the redaction itself is covered by AuditTrailObjectHistoryForReadersTest. Thread: #4580 (comment) Check: phpunit tests/Unit/Controller/ObjectsControllerTest.php + ObjectsControllerLogsRedactionTest.php OK 279 tests; tests/Unit/Controller overall: only 'could not find driver' errors (no database driver here); phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er's signal FlowRunService::signal() merges the new payload over the signal still waiting on the suspended run (array_merge: keys of both survive, a key both set takes the later value) instead of assigning it outright, so the FlowTaskBridge outcome bag and another listener's payload no longer depend on listener order. A consumed signal is still removed by persistResult(), so nothing from an earlier question reaches the merge. FlowRunServiceTest gets a two-signal case. Thread: #4580 (comment) Sweep: signal() is the only writer of the signal context key (RegistryStepDispatcher only reads and unsets it). Check: phpunit tests/Unit/Service/Flow OK 1525 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o cases decide() now treats a relation that names a decision whose record cannot be found as owned by nobody but a supervisor (a synthetic decision with no decider goes through DecisionOwnership::refusal, so a supervisor, an administrator or the owning app's listener still gets through and a reviewer is refused), and links the new decision with a compare-and-swap: EntityRelationMapper::linkDecision() gains expectedUuid and guarded arguments, and a guarded link writes only while the stored link is still the one read, otherwise it throws DecisionConflictException, which decide() rolls back and answers as decision-owned. Two reviewers taking a first decision at once can no longer both link theirs. New cases in DecisionOwnershipTest: dangling link refused for a reviewer and allowed for a supervisor, and a lost link race refused. Thread: #4580 (comment) Check: phpunit tests/Unit/Controller/DecisionOwnershipTest + EntityRelationsControllerTest + tests/Unit/Service/Decision + EntityRelationMapperUpdateDecisionMetadataTest OK 57 tests; phpcs on the three lib files clean. The guarded SQL update itself needs a database and was not run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is only logged CasePlanItemTimers::arm() now logs at error level and rethrows when the timer of a statutory (wettelijk) deadline is refused, so onEntered() raises it instead of letting the item sit active past a legal term with nothing to end it; advisory and service-norm timers stay a warning and a 0. New test: a statutory arm failure is raised, a service-norm one is not. Note for the author: CaseItemTimerListener::handle() calls onEntered() from an event listener, and Nextcloud's event dispatcher logs and swallows a listener's exception, so whether the transition itself fails depends on the caller of the transition; a visible marker on the item would need a schema field and is not added here. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/Case + tests/Unit/Listener OK 608 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s to internal callers (delete part) DeleteFileHandler::deleteFile() reads the node's id before delete() and asks the root folder for it afterwards; a node that is still there (Nextcloud ignores a listener's abortOperation() through the Node API, as the frozen-object guard uses) is logged and reported false instead of true, so FileService::deleteFile() callers such as the example-set remover and the merge no longer believe a file is gone that is not. New test: delete() that leaves the node behind answers false. The rename/move handlers are not changed here (left to the author, see the hand-back note), and the ExampleSetRemoveLiveTest case from Done-when was not added. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/File OK 489 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spec-coverage gate flagged the new release() method; it carries the same @SPEC as reserve() and the rest of the store. Thread: #4580 (comment) Check: bash scripts/run-hydra-gates.sh --scope-to-diff --base b9e5d8d -> re-run after this commit (see hand-back). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…er path CasePlanStateMachine::flushEvents() catches Throwable around every dispatchTyped after the transition is committed, so the rethrow changed only a log line. It is reverted: arm() returns false again, and the error-level log now says the item is active WITHOUT its statutory expiry timer, naming the plan item, the case object and the legal effect. No schema field is added; a visible marker on the item is for the author. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/Case OK 108 tests; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…other user's draft PermissionHandler::filterUuidsForPermissions() now asks DraftStatusPolicy::keptFrom() before the delete permission, so another user's draft is left out of a bulk delete like an object that does not exist. ObjectService::deleteObject() asks it too (RBAC on) after the object is found and before the handler runs, answering DoesNotExistException, as the save path does; the read lookup already hid it through the RBAC predicate, this is the explicit rule. Tests: bulk filter (fails at HEAD) and single delete. Thread: #4580 (comment) Check: phpunit PermissionHandlerBulkDeleteLookupTest, ObjectServiceBulkDeleteTest, ObjectServiceDraftDeleteTest and tests/Unit/Service/Object OK; phpcs on both lib files clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nly check The draft guard (Step 2b) now runs above the appendOnly gate in applyBulkSafeguards(), so a non-owner of a draft on an appendOnly schema gets the same 'Permission denied' as everywhere else instead of an appendOnly message that confirms the uuid names an existing row. New test. Thread: #4580 (comment) Check: phpunit Wave12BulkSafeguardsTest + BulkSafeguardSchemaResolutionTest OK; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dropping oldData for an entry that alone exceeds the job limit keeps the entry but skips the conditioned updated rules and the calculatedChange rules for that object (they fail closed without the previous data). The warning now says so, the docblock and comments no longer claim the notification is still sent, and the proposal, spec requirement and scenario of deferred-listener-batches-stay-under-the-job-limit are corrected; the test was renamed accordingly and asserts the log text. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/Deferral OK; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…argument WebhookService::dispatchEvent() enqueues WebhookDeliveryJob with the whole event payload as the job argument, the same 4000 (NC 32 and 33) or 32000 (34 and later) limit. A refusal from IJobList::add() was uncaught: it aborted the webhooks after it in the loop and surfaced as an exception in the event listener, where it is only a log line. Each add is now guarded: the refusal is logged at error level with the webhook id, event, payload length and message, and the loop goes on. That delivery is still lost (no redesign of webhook delivery; trimming or a payload reference is the author's call). Test: a refused first webhook does not stop the second. Thread: #4580 (comment) Check: phpunit WebhookServiceTest, WebhookAsyncDispatchTest and WebhookEventListenerTest OK; phpcs on the lib file clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w key behaviour The idempotency scenario in form-destination-validator now says same subject and same payload; two new scenarios state the 409 for another subject or payload and for a parallel retry. docs/features/form-destination.md states that the parallel-retry protection relies on Nextcloud file locking (filelocking.enabled); without it only the sequential repeat is answered from the stored answer. Thread: #4580 (comment) Check: documentation only; no test run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ys user settings docs/federated-configuration.md still sent readers to the Configuration store pane in personal settings; it says user settings now. Other languages' l10n entries stay for Transifex. Thread: #4580 (comment) Check: documentation only; git grep -i 'personal settings' -- docs finds nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the inline ternary phpcs refuses in ObjectsController::logsForReader() with an if; no behaviour change. Thread: #4580 (comment) Check: phpunit ObjectsControllerTest + ObjectsControllerLogsRedactionTest OK; phpcs on all touched lib files now clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ike the read path The three draft guards (bulk delete filter in PermissionHandler, ObjectService::deleteObject, SaveObjects bulk-save safeguard) no longer refuse an administrator: an administrator reads every draft (MagicRbacHandler bypass) and the single save already lets one through, so they use the same group check (the 'admin' group id) the handlers already use. A non-owner non-admin is still refused. Tests: admin passes the bulk delete filter, deleteObject and the bulk save; a non-owner non-admin is still kept; and the owner deletes their own draft through ObjectService::deleteObject. Thread: #4580 (comment) Check: phpunit tests/Unit/Service/Object + ObjectServiceDraftDeleteTest + ObjectServiceBulkDeleteTest + BulkSafeguardSchemaResolutionTest OK 2747 tests; phpcs on the three lib files clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 15, 2026
Open
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ❌ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ✅ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ 26239/26246 · 25 skipped | ||||
| Newman | ✅ 714/714 | ||||
| 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 locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-15 17:44 UTC
Download the full PDF report from the workflow artifacts.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes for the review of #4580 (#4580 (review)). Merge into
development; the next sync PR carries them to beta. Nothing here is merged by the reviewer.Findings → commits
59794316c6402a14a9a9728c921b612d0dbae3fbc5354845b3d942f683a9f67de4564af1f9c1d83a02b92226c4fb4be1d92759f9acaccbJobList::MAX_ARGUMENT_JSON_LENGTHwhen the server defines it (34+), else 4000; an entry that alone is too large is kept withoutoldDataand the log names the rules that are then skipped. A refused webhook job argument no longer aborts the other webhooks.227bc52ed7upload()applies the form's audience likesubmit(); a file property withoutmaxSizeis capped at 20 MiB.94854b289f224da09c486703ff76a5f990f3dd5233f3e3fb57unpublish-unsupported, notunpublished.c967b2d6bd1dfdcd386f363e6075c7_status.03e338eb67d4aa263863a33e49b49fallow().4e900c4582902c28b14cGET /logsget the redacted history #4614 introduced (page shape withoutnext/prev); admins unchanged.5faeef95829420851e65e19745bc96grant_manageholder can no longer grant toremote/remote_groupprincipals.d0d2725b3271f4796dc4DeleteFileHandler::deleteFile()returns false when the node survives a refused delete. Rename and move: see below.1f4eafbd7309d5d985fd50bee16d8fLeft to the author (threads stay open)
$ref, and failing closed there would disable untyped references. Recommendation: refuse on a$refmismatch when one is declared, plus a per-rule allowlist for untyped references.grant_managelist change. Recommendation: oneAuditTrailMapperhelper called fromObjectSharingService,ObjectOwnershipService::setOwnerGroupandOrganisationController::update, modelled onownership_handover.SchemasController. The importer,SchemaImportController,SchemaTool,SchemasToolProviderandSchemaServiceneed a decision on what refuse means for a write without a response. Recommendation: a schema-updated listener with a controller-suppression flag, unpublish-only on non-request paths.ExampleSetRemoveLiveTesthas no case for it.CasePlanStateMachine::flushEvents()swallows listener exceptions after the transition commits, so a statutory item can stay active without its expiry timer. A visible marker on the item needs a schema field.SystemOperationContext(the read path also exempts that one,MagicRbacHandler::isTrustedSystemCaller). No current caller hits it (system jobs pass_rbac:false); add the same exemption if a system-context flow should touch other users' drafts.Tracked elsewhere
Checks run on this branch
bash hydra/scripts/run-hydra-gates.sh --scope-to-diff --base b9e5d8df40→ exit 0, COVERAGE 65 of 65 applicable gates ran (32 not applicable)vendor/bin/phpuniton every touched test file, thentests/Unit/{Service,Controller,Listener,Db,Event}→ no failures other than 61could not find driver/not loadableerrors (no database in this environment; the same errors occur without these changes);tests/Unit/Service/Object+ the draft-delete and bulk tests after the last commit → OK (2747 tests)vendor/bin/phpcs --standard=phpcs.xml --warning-severity=0on every changedlib/file → cleanpre-pushcommits aboveEntityRelationMapper::linkDecision(eng-f9), a real parallel idempotent submit (eng-f3)🤖 Generated with Claude Code