Skip to content

feat(phpstan): preserve array key/value types in Arrayy\create() - #195

Open
voku wants to merge 2 commits into
masterfrom
ccr-31d00e32-pc2xer
Open

voku wants to merge 2 commits into
masterfrom
ccr-31d00e32-pc2xer

Conversation

@voku

@voku voku commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Refs #194

What

Arrayy\create() (src/Create.php) now binds its input to a template and returns a conditional Arrayy type via @phpstan-param / @phpstan-return (key-of / value-of). Array inputs keep precise key and value types. Traversable, callable, scalar, null and object inputs keep the previous untyped fallback.

Annotation-only: no runtime or API change, the native @return is unchanged, and there is no new suppression, ignoreErrors entry or baseline entry. The existing inner @var cast restates the same conditional type.

Input Before After
array<string, DateTimeImmutable> Arrayy<int|string, mixed, …> Arrayy<string, DateTimeImmutable, …>; first() is DateTimeImmutable|null
list<DateTimeImmutable> mixed elements int<0, max> keys, DateTimeImmutable values
array{id: int, name: string} mixed elements 'id'|'name' keys, int|string values
Traversable / callable / scalar / null / object untyped fallback unchanged
mixed array<int|string, mixed> data array<mixed> data (same type, spelled differently)

Tests

New fixture tests/PHPStan/CreateFactoryInference.php, run in CI next to the legacy two-parameter fixture. It covers map, list and shape inputs, first(), toList(), getIterator(), map() callback parameters, the non-array fallbacks, an unknown mixed input, and passing the result to a legacy Arrayy<int, string> consumer.

  • Red on base de7df89: 11 failures, including the legacy two-template consumer, which base rejects.
  • Green on this branch.

Local runs on PHP 8.3 (PHPStan 2.3.1 from the release phar):

  • PHPUnit: 1391 tests pass.
  • PHPStan level 8 on src/: no errors.
  • LegacyTwoParameterArrayy.php: no errors.
  • GenericNegativeUsage.php: still exactly 2 argument.type errors.
  • phpstan-fixtures.neon over tests/: 521 errors both before and after, with an identical diff (pre-existing, not gated by CI).

Not verified

  • Infection was not run locally. The sandbox has no coverage driver (pcov, Xdebug or phpdbg). GitHub Actions mutation passed on earlier exact head 0e6c394e; on current head 55035252, Infection is still running (Actions run 39158513663).
  • Only PHP 8.3 was exercised locally. GitHub Actions tests have passed for PHP 8.0–8.5, including lowest dependencies on PHP 8.0, on current head 55035252; PHPStan runs in the PHP 8.3 CI job.
  • PhpStorm/LSP support is unassessed. The conditional type is in @phpstan-return, and the plain @return is unchanged, so IDEs that ignore @phpstan-* tags will see no change.
  • Not merged. Merge needs human review and your authorization.

Process note

This was run through agent-loop (task arrayy-194, Contract revision 1 approved by voku). The prior local Loop verification was blocked by its Infection command because the sandbox lacked a coverage driver; that local state has not been altered here. A read-only Dream/ Learning triage and candidate close-out evidence are documented in PR comment, but no durable Learning decision or independent human review is recorded. Loop state is untracked and kept out of this PR.

Non-goals respected: no runtime/API change, no loosened analysis, no version bump, separate from #192 and #193.


Generated by Claude Code


This change is Reviewable

Summary by CodeRabbit

  • Developer Improvements
    • create() now provides more precise static type information, preserving array keys, values, and shapes in its result. Non-array inputs retain their existing general type behavior.
  • Tests
    • Added static analysis checks for associative arrays, lists, array shapes, non-array and mixed inputs, and compatibility with existing Arrayy usage.
    • Continuous integration now runs these checks.

Bind the input to a template and return a conditional Arrayy type so that
array inputs (maps, lists, array shapes) keep precise key and value types,
while Traversable, callable, scalar, null and mixed inputs keep the previous
untyped fallback. Annotation-only: no runtime or API change, and the native
@return remains the documented fallback.

Adds tests/PHPStan/CreateFactoryInference.php (red on de7df89, green now) and
runs it in CI next to the legacy two-parameter fixture.

Refs #194

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0137bBXkFzHNcAuxvYcpRCLG
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b2e98c25-ec6b-452c-a80f-e400d8fdde58
📥 Commits

Reviewing files that changed from the base of the PR and between 0e6c394 and 5503525.

📒 Files selected for processing: 1
  • tests/PHPStan/CreateFactoryInference.php
🚧 Files skipped from review as they are similar to previous changes: 1
  • tests/PHPStan/CreateFactoryInference.php

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Walkthrough

create() now documents input-dependent PHPStan types for array inputs and retains general types for other inputs. New inference checks cover array and non-array cases, and the CI workflow runs those checks.

Changes

Factory Type Inference

Layer / File(s) Summary
Factory annotations and inference checks
src/Create.php, tests/PHPStan/CreateFactoryInference.php, .github/workflows/ci.yml
create() annotations preserve array keys, values, and shapes in the inferred Arrayy type, while retaining general types for other inputs. PHPStan checks cover array and non-array cases, downstream methods, and compatibility with a two-template Arrayy consumer. CI runs the new checks.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 55035

The type-inference change is covered by the configured CI analysis. No issue requiring a fix before merge was identified.

Pre-merge checks: Passed 5
Check name Status Explanation
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 2 files.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: preserving array key and value types in the PHPStan annotations for Arrayy\create().
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the keys in rows
And watches values travel through
Array shapes stay in view
Mixed inputs keep their broader type
CI checks the inference trail
Then hops away beneath the moon

Comment @coderabbitai help to get the list of available commands.

@mergify

mergify Bot commented Oct 18, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@coveralls

coveralls commented Oct 18, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 92.733%. remained the same — ccr-31d00e32-pc2xer into master

@sonarqubecloud

Copy link
Copy Markdown

voku commented Oct 18, 2026

Copy link
Copy Markdown
Owner Author

arrayy-194 Dream / Learning close-out handoff (candidate evidence only)

Provenance: read-only Dream dry-run and Learning search were performed in the preceding task session, not re-run by this GitHub comment. Arrayy itself has no Learning root; the inspected existing Learning root was voku/agent-loop/.agent-loop/learning.

  • Prior Learning used: none identified for this task. No claim that prior Learning improved execution.
  • Dream: reported 79 findings, 25 proposals, 0 selected guidance/outcomes judged; one evidence_reference_unresolvable warning. That warning belongs to separate maintenance triage, not this PR's merge gate.
  • Candidate finding A (human review pending): preflight every Contract validation command and its host prerequisites before approval; the local Infection command lacked a coverage driver and blocked finish.
  • Candidate finding B (human review pending): on PHPStan 2.3.1, a mixed-accepting factory can retain array key-of / value-of via a function template and conditional @phpstan-return; the scoped positive/negative fixtures here provide source evidence. Do not generalize to IDE behavior or PHPStan: enforce TData key/value consistency without breaking two-generic consumers #192.
  • Not durable Learning: Composer zipball/phar workaround, because it is specific to the cloud environment.

No Finding, LearningNote, Proposal, Constraint or human decision is claimed to have been recorded or approved by this comment. A named human still owns any durable Learning decision and promotion. A future independent typed-factory task with observed Recall selection, L1 consumption and outcome is required for agent-loop #457.

Implementation evidence: This PR remains annotation-only. GitHub Actions on the previous exact head 0e6c394e passed PHP 8.0–8.5, PHPStan (PHP 8.3), and Infection. The new single-quote-only change 55035252 has already passed both StyleCI contexts, with its exact-head Actions run pending at the time of this note. Local Loop finish state has not been altered here.

@voku voku left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI-assisted source review on exact head 550352525d44c2b7bb2404374634aa324d1cef42 (not human approval).

I inspected the production diff, the PHPStan fixture and the CI change against #194:

  • src/Create.php changes annotations and the local static @phpstan-var only. Function parameters, return declaration and construction logic stay unchanged, so accepted runtime inputs and runtime semantics are not narrowed.
  • TCreateData is bound to the complete input. The conditional @phpstan-return preserves array key/value/shape types and retains int|string,mixed,array<int|string,mixed> for non-array inputs; the plain @return remains the portable fallback.
  • The new fixture asserts map, list, shape, first(), map(), toList(), getIterator(), mixed and non-array inputs, plus a legacy two-template consumer. CI invokes this fixture explicitly in the PHPStan job.
  • No relationship to the independent generic consistency question in #192 is claimed. PhpStorm/LSP behavior is not established by a PHPStan-only fixture.

No production-code blocker found in the narrow scope. CI on this head is running, not green yet; the former green workflow belongs to the older 0e6c394 head. A separate human review/merge decision remains required.

voku commented Oct 18, 2026

Copy link
Copy Markdown
Owner Author

Independent source review — exact head 550352525d44c2b7bb2404374634aa324d1cef42

AI review by a separate read-only reviewer that did not write this code. This is not human approval. The decision to keep this fix and merge after the review gate is the maintainer's; a human merge decision is still outstanding.

Verdict: no blocker.

  • src/Create.php: only docblock lines (@template, @phpstan-param, @phpstan-return, @phpstan-var); the function body is unchanged, so no runtime or accepted-input change.
  • The fixture is not vacuous: changing one expected type in a scratch copy (DateTimeImmutable|null → int|null) made PHPStan fail with phpstan.type and exit 1.
  • CI wiring is correct: the new fixture runs inside the existing bash -e step, ordered before the negative-usage check, on the same 8.3/basic leg as the legacy fixture.
  • Counterexamples tried on PHPStan 2.3.1 (scratch tree, repo untouched): array|string, array|null, iterable, ArrayAccess, array|Traversable and a ternary all degrade to the untyped fallback (the template is not swallowed and nothing is narrowed wrongly); array<string,array<int,string>>, non-empty-array, list, literals and array shapes stay precise; mixed does not narrow.

Non-blocking notes (not acted on, to keep this head and its CI evidence intact):

Not verified by this review: full GitHub Actions, PHPUnit, other PHP/PHPStan versions, PhpStorm/LSP behavior.

Exact-head CI (preserved): all 13 checks green on 55035252, including mutation (Infection) and PHP 8.0–8.5 (+8.0 lowest); mergeable state clean. Merge awaits the maintainer.


Generated by Claude Code

voku added a commit to voku/agent-loop that referenced this pull request Oct 18, 2026
…run (#800)

* learning: record two validated findings from the Arrayy #194 dogfood run

- finding.2026-10-18.d93598: prove each declared validation command can
  execute on the host before the Contract is approved (Infection had no
  coverage driver; verification blocked at finish).
- finding.2026-10-18.d56b74: typed-factory inference via a template on the
  whole parameter plus a conditional @phpstan-return (voku/Arrayy#195).

Findings only: no proposal, classification, note or active guidance is
created or changed. Task id ARRAYY-194 is the Loop task arrayy-194 in the
root's task-id convention.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0137bBXkFzHNcAuxvYcpRCLG

* docs(learning): remove unproven version-specific conclusion

---------

Co-authored-by: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

3 participants