Skip to content

Latest commit

 

History

History
1603 lines (1219 loc) · 237 KB

File metadata and controls

1603 lines (1219 loc) · 237 KB

L42x → Laravel 13 — Detail Per-Komponen (verified)

Data mentah verifikasi adversarial per-komponen (baca kode L42x + L13, grep count nyata). Ringkasan & roadmap ada di MIGRATION-ROADMAP.md. Ini lampiran detailnya.

Digenerate dari .migration-verified-records.json. Jangan edit tangan — regenerate.

Bagian A — Komponen existing L42x (28)


Support · flip-only / effort high

Map L13: Illuminate\Support (native, but internally split into illuminate/collections + illuminate/macroable + illuminate/conditionable + illuminate/reflection + illuminate/contracts sub-packages, all still under the Illuminate\Support namespace) Real deps (use): container, http Blockers: container, http Dep baru di L13: collections, conditionable, contracts, macroable, reflection

API delta:

Structural split, not an API rewrite. In 4.2 Support is a monolith holding Collection/Arr/Str/Contracts/MacroableTrait/Reflector/Fluent/MessageBag/ServiceProvider/Manager/helpers/Facades under one PSR-0 package (target-dir Illuminate/Support, php>=5.4). In 13 the same classes still live under namespace Illuminate\Support (Collection, Arr, Reflector) and Illuminate\Support\Traits (Macroable) so app use-statements mostly do NOT change, BUT the code has been extracted into separate composer packages: illuminate/collections, illuminate/macroable, illuminate/conditionable, illuminate/reflection, illuminate/contracts (php ^8.3). Concrete surface deltas app code can hit: (1) trait renamed MacroableTrait -> Macroable (Illuminate\Support\Traits\Macroable); the 4.2 name is gone. (2) 4.2 custom Contracts*Interface (ArrayableInterface/JsonableInterface/RenderableInterface/MessageProviderInterface) -> 13 Illuminate\Contracts\Support{Arrayable,Jsonable,Renderable,MessageProvider} (namespace + name change). (3) ServiceProvider: 4.2 had abstract register() + package()/guessPackagePath() (workbench-era); 13 makes register() concrete, drops package()/guessPackagePath(), adds booting()/booted()/publishes/mergeConfigFrom/loadRoutesFrom/loadViewsFrom/loadMigrationsFrom etc. (16.7K vs 4.2K). (4) Str/Collection/Arr gained a huge number of methods (4.2 Str.php is only ~9.7K / 24 static methods vs 13 ~68.9K) plus Stringable, Number, Uri, Sleep, Lottery, Env, Js, HtmlString are brand-new siblings. helpers.php shrank 59->23 (many array_/str_ helpers deprecated in favor of Arr::/Str:: and response()/app() helpers relocated to foundation). Facade base kept same accessor pattern (getFacadeAccessor/__callStatic/swap/shouldReceive). Also note this 4.2 fork already carries Reflector.php, Util.php, ForwardsCalls trait and an Arr class backported from later Laravel, so those specific pieces are pre-aligned.

Public API (app-facing):

  • Illuminate\Support\Collection
  • Illuminate\Support\Arr
  • Illuminate\Support\Str
  • Illuminate\Support\Fluent
  • Illuminate\Support\MessageBag
  • Illuminate\Support\ViewErrorBag
  • Illuminate\Support\ServiceProvider
  • Illuminate\Support\Manager
  • Illuminate\Support\NamespacedItemResolver
  • Illuminate\Support\Pluralizer
  • Illuminate\Support\Reflector
  • Illuminate\Support\Facades\Facade (+ App/Config/DB/Route/View/... stubs)
  • Illuminate\Support\Traits\MacroableTrait
  • Illuminate\Support\Contracts\{Arrayable,Jsonable,Renderable,MessageProvider}Interface
  • helpers.php: array_*/str_*/e/value/with/head/last/dd/etc (59 fns)

Risks:

  • ⚠️ MacroableTrait -> Macroable rename: every class doing use Illuminate\Support\Traits\MacroableTrait breaks at flip. NOTE: in THIS repo every MacroableTrait user is framework-internal (Html/View/Http/Log/Foundation/Database/Cache/Validation/Exception/Pagination + Support itself), which the flip replaces wholesale — so the real audit target is genuine app-space (dicoding) code, not src/Illuminate. Grep app-space for MacroableTrait before flipping.
  • ⚠️ Contracts move: 4.2 ArrayableInterface/JsonableInterface/RenderableInterface/MessageProviderInterface (namespace Illuminate\Support\Contracts) become Illuminate\Contracts\Support{Arrayable,Jsonable,Renderable,MessageProvider}; any class implementing the old interfaces or type-hinting them must be reshaped. (4.2 also ships ResponsePreparerInterface here, not in the record's publicApi list.)
  • ⚠️ ServiceProvider::package()/guessPackagePath() removed (confirmed present in 4.2 at lines 54/106, register() abstract at line 44) — any 4.2 workbench-style provider using package()/publishes-via-package must be rewritten to publishes()/loadViewsFrom()/mergeConfigFrom(); register() no longer abstract.
  • ⚠️ Deprecated helper functions (str_, array_, e already differs) removed from helpers.php (4.2 has 59) — app calls to removed global helpers fail; must migrate to Str::/Arr:: or restore shims.
  • ⚠️ PHP floor jumps 5.4 -> 8.3: Support pulls doctrine/inflector ^2, nesbot/carbon ^3, voku/portable-ascii ^2 — none present in the 4.2 tree (4.2 require-dev is jeremeamia/superclosure + patchwork/utf8); Pluralizer switches from patchwork to doctrine/inflector, changing some singular/plural edge cases.
  • ⚠️ Support is imported by nearly every other component, so it must be flipped as part of the coordinated core flip, not incrementally; it sits at the bottom of the dependency graph.

Notes: Support is the foundational framework core and sits at the bottom of the graph: authoritative use-statement grep gives realDeps container + http only. Verified empirically: only two container/http import sites exist — container via Traits/CapsuleManagerTrait.php (use Illuminate\Container\Container) and http via Facades/Response.php (use Illuminate\Http\JsonResponse + Illuminate\Http\Response). Reflector.php references Illuminate\Container\Util only in a DOC COMMENT (it self-implements getParameterClassName over ReflectionNamedType and does NOT import Container), so container's ONLY real hard dep is CapsuleManagerTrait — the record's 'referenced by Reflector' phrasing is comment-only and slightly overstated but harmless. It cannot be swapped for a third-party lib or shimmed — it IS Illuminate. Migration is flip-only: you replace the whole 4.2 package tree with the 13 illuminate/support + extracted micro-packages (collections/macroable/conditionable/reflection/contracts) in the final flip. Namespace stability is the saving grace — Collection/Arr/Str/Reflector keep namespace Illuminate\Support and Macroable keeps Illuminate\Support\Traits, so most app use-statements survive. The high-effort part is NOT this package's internals (they come from upstream) but auditing genuine app-space for the four concrete breaks: MacroableTrait rename, old Contracts\*Interface, ServiceProvider::package(), and removed global helpers — all in-repo occurrences of these are framework-internal and evaporate with the flip. Reflector already exists in this 4.2 fork (backported) and maps to Illuminate\Support\Reflector (same namespace in both). Effort=high because of the wide blast radius across every consumer, not per-class complexity.

Verify note (koreksi adversarial): Confirmed. Re-ran authoritative grep: prefixes = Container, Http, Support (Support self-referential) -> realDeps [container, http] complete, no hallucination, nothing missing. Located the exact import sites (CapsuleManagerTrait->Container; Facades/Response->Http\JsonResponse+Response) and confirmed Reflector's Container mention is a doc-comment only (no import) — clarified 'container dep = CapsuleManagerTrait, not Reflector' in notes. Verified 4.2 composer.json (php>=5.4, psr-0, target-dir, dev-deps superclosure+patchwork), MacroableTrait name (not Macroable), Contracts*Interface naming (+undocumented ResponsePreparerInterface), ServiceProvider abstract register()/package()/guessPackagePath() at lines 44/54/106, helpers.php 59 fns, Str.php only 24 static methods (~9.7K). migrationClass=flip-only justified (it IS Illuminate, replaced wholesale, not delegating to a maintained primitive and not shimmable — so not 'done'). effort=high defensible given blast radius (though internals are zero-port). blockers [container, http] == realDeps not-yet-done, correct. No factual corrections to schema fields; only tightened risk/notes wording (comment-only container ref; in-repo MacroableTrait/Contracts users are framework-internal, so app-space is the true audit target; noted this fork pre-backports Reflector/Util/ForwardsCalls/Arr). No L13 reference tree or vendor/illuminate present in-repo, so L13-side package-split/php^8.3/method-growth claims accepted as historically accurate but unverifiable here.


Container · flip-only / effort medium

Map L13: Container Real deps (use): support Blockers: support Dep baru di L13: contracts, reflection, log

API delta:

L42x is already a heavily-backported modern container (verified from Container.php, not composer.json — the manifest still lists php >=5.4 but the code uses PHP 8 typed properties and nullable params). It carries PSR-11 get/has (implements Psr\Container\ContainerInterface), contextual when()/ContextualBindingBuilder, bindMethod/BoundMethod, singletonIf, beforeResolving/afterResolving, factory, wrap, makeWith, rebinding/refresh, tags, RewindableGenerator — plus legacy share/isShared/bindShared/resolvingAny not present in L13. L13 is a near-superset adding: (1) attribute-based injection — Attributes/ dir (Auth, Config, DB, Storage, Cache, Log, Context, Give, Bind, BindWhen, Scoped, Singleton, RouteParameter, etc.), whenHasAttribute(), resolveFromAttribute(), afterResolvingAttribute(); (2) scoped bindings — scoped()/scopedIf()/forgetScopedInstances(); (3) environment helpers; (4) currentlyResolving(); (5) SelfBuilding contract + Support\Traits\ReflectsClosures trait. Exceptions relocated: BindingResolutionException & CircularDependencyException are DELETED from illuminate/container and now come from illuminate/contracts (Container.php + BoundMethod.php import Illuminate\Contracts\Container{BindingResolutionException,CircularDependencyException,ContextualAttribute,SelfBuilding}); EntryNotFoundException stays LOCAL in illuminate/container but now implements Psr\Container\NotFoundExceptionInterface. reflection coupling: NOTE the container's Util class stays local (Container/Util.php) and Reflector is not referenced by the container at all; the real illuminate/reflection dependency is the ReflectsClosures trait, which is namespaced Illuminate\Support\Traits but physically shipped by the illuminate/reflection package. Min PHP declared 5.4 (stale) -> 8.3.

Public API (app-facing):

  • make
  • makeWith
  • get
  • has
  • bind
  • bindIf
  • singleton
  • singletonIf
  • instance
  • extend
  • bound
  • resolved
  • alias
  • isAlias
  • tag
  • tagged
  • when
  • addContextualBinding
  • call
  • wrap
  • factory
  • bindMethod
  • callMethodBinding
  • hasMethodBinding
  • beforeResolving
  • resolving
  • afterResolving
  • rebinding
  • refresh
  • build
  • flush
  • getBindings
  • getAlias
  • forgetInstance
  • forgetInstances
  • forgetExtenders
  • share
  • isShared
  • bindShared
  • resolvingAny
  • offsetGet/Set/Exists/Unset

Risks:

  • ⚠️ Exception namespace relocation: L42x defines BindingResolutionException/CircularDependencyException locally under Illuminate\Container; L13 pulls them from Illuminate\Contracts\Container. Any app or core catch/throw referencing the FQCN Illuminate\Container\BindingResolutionException (or CircularDependencyException) breaks at flip — must sweep call-sites. EntryNotFoundException stays under Illuminate\Container (no FQCN break) but gains implements Psr\Container\NotFoundExceptionInterface.
  • ⚠️ illuminate/reflection is a hard composer require of L13 illuminate/container, but the linkage is subtle: the container has NO in-file use Illuminate\Reflection\... statement — Reflector is unused and Util is local. The dependency is the ReflectsClosures trait, namespaced Illuminate\Support\Traits but shipped by illuminate/reflection. If Support is flipped independently this trait must resolve from the reflection package, not Support.
  • ⚠️ L13 requires PHP 8.3; the whole flip is gated on the runtime upgrade (queue/PHP-upgrade track).
  • ⚠️ Container is the DI kernel every provider/facade binds through — cannot be swapped incrementally, so it drags Contracts+Reflection+Support to be L13-shaped in the same flip window.
  • ⚠️ Attribute injection + scoped bindings are additive; unlikely to break L42x app code. illuminate/log is only a composer 'suggest' (needed by the optional Log/Context attributes, imported in Attributes/Context.php, not Container.php) so it is not a hard install blocker. Any custom Container subclass in app-space must match widened L13 method signatures.
  • ⚠️ L42x-only legacy methods share()/isShared()/bindShared()/resolvingAny() do NOT exist in L13; if app or core code calls them they break at flip (they are the only true public-API regressions — everything else is superset).

Notes: Pure framework-core DI container — the resolution kernel. Cannot be incrementally swapped behind an adapter; it moves whole at the final flip. The L42x tree already carries a modern backport whose public surface is a near-superset of what app code touches (make/bind/singleton/instance/when/call/tag/PSR-11), so the flip is largely a drop-in file replace, NOT a port. Real flip work is namespace realignment: (a) delete the local Container exception classes (BindingResolutionException, CircularDependencyException) and repoint catch/throw sites to Illuminate\Contracts\Container equivalents; EntryNotFoundException stays local, (b) accept L13's wider Container.php (attributes, scoped, environment helpers) which are additive, (c) note reflection resolves via the ReflectsClosures trait shipped by illuminate/reflection (Util stays local; Reflector is unused by the container). Watch the L42x-only legacy methods share/isShared/bindShared/resolvingAny which L13 drops. Authoritative L42x realDeps from use-statements = support only (Arr, Reflector, Util) + external Psr\Container (not an Illuminate component). Blocker is therefore just Support being L13-shaped; Contracts, Reflection and Log are NEW couplings introduced by the L13 version (Log only via optional attributes / composer suggest).

Verify note (koreksi adversarial): Verified empirically against both trees (L42x + /home/agis/www/framework@v13.30.1, a real L13). realDeps CONFIRMED = support only (grep of use Illuminate\\... = Illuminate\Support\{Arr,Reflector,Util}, plus Psr\Container which is not an Illuminate component). blockers=[support] and flip-only/medium all CONFIRMED. CORRECTIONS to the prior record's mechanism claims: (1) the L13 container does NOT import Reflector or Util from illuminate/reflection — Util is LOCAL (Container/Util.php) and Reflector is never referenced by the container; the real reflection dependency is the ReflectsClosures trait (namespaced Illuminate\Support\Traits but physically shipped by illuminate/reflection at Reflection/Traits/ReflectsClosures.php), so the prior risk wording 'Reflector/Util must resolve from reflection' was inaccurate though the reflection-is-a-hard-dep conclusion stands. (2) EntryNotFoundException is NOT deleted/relocated — it stays in illuminate/container and now implements Psr\Container\NotFoundExceptionInterface; only BindingResolutionException & CircularDependencyException move to Contracts. (3) Added the L42x-only legacy methods share/isShared/bindShared/resolvingAny to publicApi and risks — these are real public-API regressions L13 drops. (4) Clarified illuminate/log is composer 'suggest' (Attributes/Context.php), not a hard require. l13RealDeps kept as contracts/reflection/support/log: contracts+log are genuine in-file use-statements, reflection is a genuine hard composer require via the relocated trait.


Http · flip-only / effort medium

Map L13: Http Real deps (use): session, support Blockers: session, support Dep baru di L13: contracts, collections, conditionable, macroable, container, database, pagination, image

API delta:

Public app-facing surface (Request::input/only/except/all/json/ajax/wantsJson/session, Response::setContent/getOriginalContent morph-to-JSON, RedirectResponse::with/withInput/withErrors/onlyInput, JsonResponse::get-setData) is stable and preserved. Internal reshape in L13: Request splits its body into Concerns\ traits (InteractsWithInput, InteractsWithContentTypes, InteractsWithFlashData, CanBePrecognitive), now implements Arrayable+ArrayAccess and mixes in Macroable+Conditionable. L42x Response/JsonResponse/RedirectResponse use the legacy Illuminate\Support\Contracts{Arrayable,Jsonable,Renderable,MessageProvider}Interface namespace; L13 uses Illuminate\Contracts\Support{Arrayable,Jsonable,Renderable,MessageProvider}. RedirectResponse uses Session\Store + Uri. New in L13: whole Client/ (Guzzle-based HTTP client Factory/PendingRequest/Pool), Testing/ (fakes), UploadedFile+File+FileHelpers, StreamedEvent, Middleware/, Resources/, Exceptions/. FrameGuard is removed. PHP floor moves ^8.3; constructors gain typed/named args (L42x already partially there: symfony 6.4, typed/static returns, #[\Override]).

Public API (app-facing):

  • Request (extends SymfonyRequest): instance/method/root/url/fullUrl/path/segment(s)/is/ajax/secure/ip(s)/exists/has/all/input/only/except/query/cookie/file/hasFile/header/server/old/flash*/merge/replace/json/isJson/wantsJson/format/session/createFromBase
  • Response (extends SymfonyResponse, uses ResponseTrait): setContent/getOriginalContent/morphToJson/shouldBeJson/->original
  • RedirectResponse (extends SymfonyRedirectResponse): with/withInput/onlyInput/exceptInput/withErrors/withCookie(s)/header/get-setRequest/get-setSession/__call(withX via starts_with+snake_case)
  • JsonResponse (extends SymfonyJsonResponse): getData/setData/get-setJsonOptions
  • ResponseTrait: header/withCookie
  • FrameGuard (HttpKernelInterface decorator adding X-Frame-Options: SAMEORIGIN)

Risks:

  • ⚠️ L42x references the legacy Illuminate\Support\Contracts* namespace (ArrayableInterface/JsonableInterface/RenderableInterface/MessageProviderInterface) inside Response/JsonResponse/RedirectResponse; these move to Illuminate\Contracts\Support* in L13. Since these are internal to the Http component they are replaced wholesale on flip, but any OTHER core/app code type-hinting the old FQNs breaks unless Support ships aliases.
  • ⚠️ FrameGuard evaporates in L13 (no native equivalent in Http). If the app registers/relies on Illuminate\Http\FrameGuard to set X-Frame-Options: SAMEORIGIN, that must be re-expressed as app middleware before the flip or the header silently disappears.
  • ⚠️ RedirectResponse::__call uses global helpers starts_with()/snake_case(); Request uses array_get/array_set/array_forget/str_is (from L42x Support helpers.php). Removed in modern Laravel (now Str::/Arr::). Will fatal unless Support still provides global-helper shims -- hence Support is a genuine blocker.
  • ⚠️ L13 Request implements ArrayAccess + Arrayable; offsetExists/offsetGet route through the route resolver; subtle behavioral change if app code does isset($request['x']).
  • ⚠️ L13 illuminate/http hard-requires Guzzle (Client/), fruitcake/php-cors, symfony/mime, psr/http-message; not used by L42x but become hard composer deps of the component. image is only a composer 'suggest', but the source uses Illuminate\Image\Image so it is a real code dep when uploaded-file image() is used.

Notes: Record VERIFIED accurate. L42x Http is already substantially modernized: composer.json requires symfony/http-foundation ~6.4 and symfony/http-kernel ~6.4, classes extend Symfony 6.4 (MAIN_REQUEST, ResponseHeaderBag, InputBag, typed/static returns, #[\Override]). Pure framework core wrapping Symfony HttpFoundation with hand-written Illuminate delegation methods (NOT a thin done-style passthrough to a maintained primitive -- so 'done' would be wrong; the Illuminate surface is bespoke and flips wholesale with the framework). realDeps confirmed = only session + support (authoritative from use-statements; note Illuminate\Support\Contracts\* is a SUB-namespace of Support in 4.2, not the separate illuminate/contracts package, so 'contracts' correctly does NOT appear in L42x realDeps but correctly DOES appear as a newL13Dep). Both realDeps (session, support) are still 4.2-era in this repo (php >=5.4, legacy helpers.php, Support\Contracts namespace) => not done => both are legitimate blockers, matching the rule 'blockers = realDeps not yet done'. Real migration work: (1) namespace-fix legacy Support\Contracts\Interface refs to Illuminate\Contracts\Support\ (arrives free by taking L13 component; blocked on Support/Contracts being L13-shaped), (2) drop FrameGuard as evaporate/app-space middleware, (3) ensure starts_with/snake_case/array_* helper path survives Support helper changes. Everything else (Concerns split, Client, Testing, UploadedFile, Macroable/Conditionable mix-ins) arrives for free at flip.

Verify note (koreksi adversarial): Confirmed. Re-ran the grep: L42x Http imports exactly Illuminate\Session + Illuminate\Support -- realDeps [session, support] is complete with no hallucinated/missing entries. Read all 7 L42x files: verified FrameGuard sets X-Frame-Options:SAMEORIGIN, Response/JsonResponse use legacy Illuminate\Support\Contracts* FQNs, RedirectResponse::__call uses starts_with()/snake_case(), Request uses array_get/str_is helpers. L13 side is framework 13.30.1 at /home/agis/www/framework/src/Illuminate/Http; its composer hard-requires collections/conditionable/macroable/session/support(+guzzle/psr/symfony-mime) and its source use-statements confirm Container/Contracts/Database/Image/Pagination/Session/Support -- l13RealDeps and newL13Deps both accurate. flip-only justified (bespoke Illuminate surface replaced wholesale, NOT delegated to a maintained primitive so 'done' rejected; legacy-contract fix is internal-to-component and resolves free on flip so 'reshape-callsites' not warranted). effort medium sane vs the FrameGuard-evaporate + helper-shim + contract-namespace work and 8-new-dep fan-out. blockers [session, support] exactly = the realDeps, both still 4.2-era (not done). Minor precision note added: image is a composer 'suggest' not a hard require, but is a genuine code-level use so kept in l13RealDeps. No field required correction.


Session · shim-symfony / effort medium

Map L13: Session (illuminate/session) Real deps (use): cache, console, cookie, database, filesystem, support Blockers: support, filesystem, cache, cookie, database, console Dep baru di L13: auth, contracts, http, routing

API delta:

L42x Store implements Symfony HttpFoundation SessionInterface directly and carries the full Symfony bag machinery (registerBag/getBag/getMetadataBag/getBagData, MetadataBag, SessionBagInterface) implemented natively over $this->attributes (loadSession/readFromHandler) — it conforms to the Symfony interface but does NOT delegate to a Symfony session engine. L13 Store instead implements Illuminate\Contracts\Session\Session (native contract) and pushes Symfony interop into a separate SymfonySessionDecorator (implements Symfony SessionInterface); the bag methods are gone from Store. L13 adds a large method surface absent in 4.2: only/except/missing/hasAny/remember/increment/decrement/now/id/setHandler plus enum-typed flash(BackedEnum|UnitEnum|string) and UnitEnum|string keys throughout. Middleware is completely reshaped: L42x Middleware implements Symfony HttpKernelInterface with handle($request,$type,$catch) (old Stack style); L13 replaces it with pipeline middleware Middleware/StartSession + Middleware/AuthenticateSession (implements AuthenticatesSessions) using handle($request, Closure $next). L13 also adds ArraySessionHandler, NullSessionHandler, EncryptedStore. composer.json: L42x requires illuminate/cache+cookie+encryption+support (encryption is a stale composer require — no use-statement references it); L13 require = illuminate/collections+contracts+filesystem+support (cache/cookie/database now via Illuminate\Contracts, console demoted to suggest), php ^8.3, ext-ctype, ext-session, symfony/http-foundation ^7.4/^8.

Public API (app-facing):

  • Store::start/save/get/put/has/pull/flash/reflash/keep/all/remove/forget/flush/regenerate/migrate/invalidate
  • Store::token/getToken/regenerateToken/previousUrl/setPreviousUrl
  • Store::getHandler/handlerNeedsRequest/setRequestOnHandler/setExists
  • Store::registerBag/getBag/getMetadataBag/getBagData (Symfony bag API)
  • SessionManager::getDefaultDriver/setDefaultDriver/getSessionConfig (extends Support\Manager)
  • Middleware (HttpKernelInterface, Stack-style session middleware)
  • SessionInterface extends Symfony BaseSessionInterface
  • TokenMismatchException

Risks:

  • ⚠️ Store's implements clause moves from Symfony SessionInterface to Illuminate\Contracts\Session\Session; any app or framework code type-hinting the Symfony interface or calling registerBag/getBag/getMetadataBag/getBagData breaks and must route through the new SymfonySessionDecorator
  • ⚠️ Middleware reshape is not incremental: L42x Middleware implements HttpKernelInterface (Stack, handle($request,$type,$catch)) while L13 uses pipeline middleware (handle($request, Closure $next)); the HTTP kernel/dispatch wiring (http/routing) must be L13-shaped for this to flip, so it is coupled to the final kernel flip
  • ⚠️ L13 introduces new coupling to auth (AuthenticateSession implements AuthenticatesSessions), http (Request) and routing (Route/previousRoute) that did not exist in the 4.2 Session package — these become blockers only for the L13 middleware, not for the Store data API
  • ⚠️ L13 requires PHP ^8.3, ext-ctype, ext-session, symfony/http-foundation ^7.4/^8; the L42x copy is on symfony ~6.4 — a Symfony major bump rides along with this component
  • ⚠️ flash() gains BackedEnum|UnitEnum|string typing and Store gains increment/decrement/remember/now/only/except/missing/hasAny — behavioral parity of any locally-relied-on flash/old-input/CSRF semantics should be spot-checked

Notes: This L42x copy is ALREADY substantially Symfony-shaped and partially modernized: Store implements Symfony\Component\HttpFoundation\Session\SessionInterface, uses MetadataBag/SessionBagInterface/Cookie/Request/Response from HttpFoundation, and composer.json already pins symfony/http-foundation ~6.4, symfony/finder ~6.4, nesbot/carbon ^2.71 with PHP-8-style return types (bool/string/array/mixed) already on the methods. But note Store implements the Symfony interface with NATIVE logic (loadSession/readFromHandler over $this->attributes + hand-rolled bag machinery) — it is not wrapping a Symfony session engine. The migration matches the roadmap's own shim-symfony classification (MIGRATION-ROADMAP.md line 173/205-206: HttpFoundation Session is already the backing dependency and stays so in L13). Remaining work: (1) swap Store's implements clause from Symfony SessionInterface to Illuminate\Contracts\Session\Session and move Symfony bag interop into SymfonySessionDecorator, (2) replace the HttpKernelInterface Stack-style Middleware with pipeline StartSession/AuthenticateSession, (3) reconcile the expanded Store API. Store's public data API (get/put/flash/etc.) is unchanged in spirit so app call-sites mostly keep working; breakage is at Middleware wiring and any code using the Symfony bag API or type-hinting the Symfony SessionInterface. console is a genuine realDep (Console/SessionTableCommand.php: use Illuminate\Console\Command, wired via CommandsServiceProvider) and is NOT done (roadmap marks console 'reshape'), so it remains a blocker even though L13 demotes it to composer 'suggest'.

Verify note (koreksi adversarial): Corrected 3 fields. (1) blockers: ADDED 'console' — it is a real use-statement dep (src/Illuminate/Session/Console/SessionTableCommand.php: use Illuminate\Console\Command, wired by CommandsServiceProvider) and is NOT done (MIGRATION-ROADMAP.md line 173 lists console among session deps; line 169 marks console 'reshape'), so per the 'blockers = realDeps not yet done' rule it belongs; the record's 'dev-only/suggest' excuse does not remove it. blockers now = all 6 realDeps (only encryption+hashing are 'done' repo-wide, roadmap lines 43/167-168). (2) l13RealDeps: removed 'collections' (not a use-statement dep — Collection/Arr come via Illuminate\Support; collections is composer-require only), and removed 'cache'+'cookie' (actual L13 use-statements are Illuminate\Contracts\Cache and Illuminate\Contracts\Cookie → 'contracts', not the concrete components). Authoritative grep of L13 use Illuminate\X = auth,console,contracts,database,filesystem,http,routing,support. (3) newL13Deps: removed 'collections' → auth,contracts,http,routing. UNCHANGED and confirmed empirically: realDeps (grep exact = cache,console,cookie,database,filesystem,support), migrationClass shim-symfony (matches roadmap line 173 + reasoning 205-206; but note Store implements Symfony iface with native logic, does not wrap a Symfony engine — added to apiDelta/notes), effort medium (roadmap line 173), l13Mapping native. L13 class signatures verified in /home/agis/www/framework/src/Illuminate/Session: Store implements Session (Illuminate contract), SymfonySessionDecorator implements Symfony SessionInterface, StartSession/AuthenticateSession pipeline handle($request, Closure $next).


Cache · reshape-callsites / effort high

Map L13: native (same illuminate/cache package) Real deps (use): console, database, encryption, filesystem, redis, support Blockers: support, contracts, database, filesystem, redis, console Dep baru di L13: contracts

API delta:

StoreInterface -> Illuminate\Contracts\Cache\Store (moved to contracts, getPrefix/put reshaped, many/putMany added). Repository now implements ArrayAccess + PSR-16 CacheInterface + Cache contract; grows to 49 public methods (many, putMany, missing, flexible, funnel, touch, typed getters string/integer/float/boolean/array, get/set/delete PSR-16 aliases, events via Dispatcher). CRITICAL: TTL unit changed from MINUTES (L42x Repository::getMinutes, $default=60) to SECONDS (L13 Repository::getSeconds) on put/add/remember — silent 60x behavior change at every call-site. New subsystems added: Locks (Lock/CacheLock/DatabaseLock/RedisLock/ArrayLock/FileLock/DynamoDbLock/atomic locks), RateLimiter + RateLimiting/ + Limiters/, Events/, DynamoDbStore, MemoizedStore, FailoverStore, SessionStore, StorageStore, PSR-16 bridge (cache.psr6 via symfony/cache). MacroableTrait -> illuminate/macroable (still referenced as Illuminate\Support\Traits\Macroable). DatabaseStore no longer uses Encrypter (encryption dep dropped, confirmed). ServiceProvider now implements DeferrableProvider, binds cache.psr6 + RateLimiter::class in provides.

Public API (app-facing):

  • CacheManager::store()/driver()/extend()/getDefaultDriver()
  • Repository::get()/put()/add()/remember()/rememberForever()/pull()/has()/forget()/flush()
  • Repository::increment()/decrement()/forever()/sear()
  • StoreInterface (get/put/increment/decrement/forever/forget/flush/getPrefix)
  • TaggableStore::tags()/section()
  • Facade Cache::

Risks:

  • ⚠️ TTL unit flip minutes->seconds: every Cache::put/add/remember with a numeric TTL silently caches 60x shorter unless rewritten — highest-impact silent regression (verified: L42x getMinutes/$default=60 vs L13 getSeconds)
  • ⚠️ StoreInterface -> Contracts\Cache\Store rename breaks any app-space custom cache driver implementing the old interface
  • ⚠️ Repository::getPrefix()/setPrefix() and getDefaultCacheTime semantics (minutes vs seconds) changed
  • ⚠️ DatabaseStore dropped Encrypter — cached DB values previously encrypted are no longer, migration/read compatibility for existing cache table rows (verified L13 DatabaseStore has zero Encrypter references)
  • ⚠️ sear() still exists but everything else expanded; app code relying on absent methods (many/lock) simply didn't exist in 4.2 so low risk there
  • ⚠️ Facade array-access and macros still work but Macroable trait moved to illuminate/macroable package (namespace unchanged in use-statements)

Notes: Framework-core plumbing behind the Cache:: facade — same illuminate/cache package on both sides, replaced wholesale at the flip, but NOT drop-in because of the minutes->seconds TTL change and the Store-contract rename. Cannot swap the engine incrementally under the L42x API. Practical path: (1) audit and reshape app call-sites now — pin numeric TTLs to explicit seconds (or DateTime/Carbon intervals, which are unit-safe on both), and migrate any custom Store implementations to the new contract signature; (2) flip the engine at the end with the rest of core. The Lock/RateLimiter/Events/DynamoDb/Memoized/Failover/Session/Storage subsystems are pure additions — nothing to migrate, they just become available. L42x console commands (ClearCommand/CacheTableCommand) are superseded by L13's Console/ dir. Blockers are exactly the L42x realDeps that must be L13-shaped first (support, contracts, database, filesystem, redis, console); encryption is intentionally NOT a blocker since L13 DatabaseStore drops it. Note: contracts is a blocker because the new Store contract lives there, even though it is not an L42x realDep of Cache itself.

Verify note (koreksi adversarial): Corrected l13RealDeps and newL13Deps. l13RealDeps field is defined as "imports via use-statements"; the authoritative L13 grep (grep -rhoE "use Illuminate\[A-Za-z]+" on /home/agis/www/framework/src/Illuminate/Cache) yields exactly console, contracts, database, filesystem, redis, support. collections and macroable are NOT use-statement imports — L13 still writes use Illuminate\Support\Collection and use Illuminate\Support\Traits\Macroable; they are genuine composer require entries (illuminate/collections, illuminate/macroable in composer.json) but pulled in transitively under the Illuminate\Support\ namespace, so they do not belong in a use-statement-derived dep list. Removed them from l13RealDeps and reduced newL13Deps to just [contracts] (the only genuinely-new use-statement namespace vs L42x). Everything else confirmed by reading the actual files: L42x realDeps grep = cache(self)+console+database+encryption+filesystem+redis+support (self correctly excluded, no hallucinations); L42x Repository uses minutes (getMinutes/$default=60), StoreInterface is a local interface with getPrefix(), DatabaseStore uses Encrypter; L13 Repository implements ArrayAccess+CacheContract with 49 public methods and getSeconds (TTL flip confirmed); L13 Store contract at Contracts/Cache/Store.php; L13 ServiceProvider is DeferrableProvider binding cache.psr6+RateLimiter::class; L13 DatabaseStore has zero Encrypter references; Locks/RateLimiter/DynamoDb/Memoized/Failover/Session/Storage subsystems all present in L13. migrationClass reshape-callsites, effort high, and blockers (L42x realDeps minus encryption) all justified and confirmed.


Database · flip-only / effort very-high

Map L13: Database Real deps (use): cache, console, container, events, filesystem, support Blockers: broadcasting, console, container, contracts, events, filesystem, http, pagination, queue, support Dep baru di L13: broadcasting, contracts, http, pagination, queue

API delta:

Same conceptual component (Eloquent ORM + query/schema/migration/connection), but the L13 surface exploded. Model went from implements ArrayAccess, ArrayableInterface, JsonableInterface, JsonSerializable to implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToString, HasBroadcastChannel, Jsonable, JsonSerializable, QueueableEntity, Stringable, UrlRoutable (both signatures verified in source). Big additions: Casts/, Attributes/ (PHP 8 attribute-driven config: Table, Connection, Scope, RouteKey...), Factories/, Concerns/ traits, Eloquent Prunable/MassPrunable, BroadcastsEvents, HasBuilder/HasCollection generics. L42x Eloquent/ was flat (Builder, Collection, Model, Relations, ScopeInterface, SoftDeletingScope, SoftDeletingTrait); L13 adds all the above. New infra: DatabaseTransactionsManager/DatabaseTransactionRecord (nested tx + afterCommit), LostConnectionDetector/DetectsLostConnections, ConcurrencyErrorDetector/DetectsConcurrencyErrors, Events/ dir. L42x's SoftDeletingTrait/ScopeInterface became SoftDeletes/Scope. Contracts extracted out. PHP floor >=5.4 (composer) -> ^8.3. Cache dependency (Capsule CacheManager, remember-query-cache) present in 4.2, dropped entirely in L13.

Public API (app-facing):

  • Eloquent\Model (create, find, all, save, delete, where, with, hasMany/belongsTo/etc relations, $fillable/$guarded/$casts, global+local scopes)
  • Eloquent\Builder (query builder over models)
  • Eloquent\Collection
  • Query\Builder (fluent query)
  • Schema\Builder + Blueprint (migrations)
  • Connection / ConnectionInterface (select/insert/update/statement/transaction)
  • DatabaseManager (ConnectionResolverInterface)
  • Migrations\Migration + Seeder
  • Capsule\Manager (standalone bootstrap)
  • DatabaseServiceProvider / MigrationServiceProvider

Risks:

  • ⚠️ Enormous app-side blast radius: nearly every app model/query/migration touches this; Eloquent 4.2 idioms (->lists(), ArrayableInterface, SoftDeletingTrait, old pagination, magic getters) must be reshaped at every callsite.
  • ⚠️ Behavioral changes not caught by signatures: default timestamp casting, $dateFormat, null handling, empty-string vs null attributes, strict-mode style behaviors, DatabaseTransactionsManager afterCommit semantics that did not exist in 4.2.
  • ⚠️ Grammar/schema SQL differences across the 4.2->13 gap can silently alter generated DDL/queries; needs migration + query regression testing against the real DB.
  • ⚠️ New hard coupling to contracts/pagination/queue/broadcasting/http means those must be in place; a partial flip breaks model serialization/queueing/broadcasting.
  • ⚠️ PHP 8.3 floor + typed properties/attributes: 4.2 models relying on loose typing or removed magic will fatal, not warn.
  • ⚠️ Removed remember-query-cache (Cache coupling gone in L13): any app relying on ->remember()/query caching via Capsule must be migrated separately.

Notes: This is the ORM/DB core — the single largest, most app-facing Illuminate component. Pure framework core: cannot be swapped for a standalone/native primitive (unlike Encryption/Hashing/Session), and cannot be shimmed behind its own API incrementally because app code depends directly on Eloquent Model/Builder/Query idioms that changed shape. Model.php is 3125 lines (L42x) / 2957 (L13) — no delegation to a maintained primitive exists, so 'done' is impossible and flip-only is correct. It moves only in the final atomic flip together with the rest of the vendored framework. App-side callsite reshaping (Eloquent 4.2 -> 13 idioms: $casts, attribute mutators, SoftDeletes vs SoftDeletingTrait, pagination return types, relation signatures, Collection generics, removed ->lists()) is real app-space work tracked at callsites, not a swap of this component. Blockers = the L13 dependency set (l13RealDeps): the whole foundation (container, support, contracts, events) plus peripheral packages Eloquent now couples to (pagination, queue, broadcasting, http for API Resources, console+filesystem for migrations). realDeps is authoritative from L42x use-statements (verified): cache/console/filesystem are dev-time (Capsule cache, console, migrations) but present.

Verify note (koreksi adversarial): Verified against source. realDeps CONFIRMED complete/accurate: per-component grep of use Illuminate\... in L42x Database yields exactly cache, console, container, events, filesystem, support (plus self-ref Database) — no hallucinated or missing entries. Note: the check's own regex counts self-references and needs per-file dedup, but the authoritative distinct set matches. l13RealDeps CONFIRMED (grep of L13 tree at /home/agis/www/framework = 13.0.x-dev): broadcasting, console, container, contracts, events, filesystem, http, pagination, queue, support. newL13Deps CORRECTED to canonical set order [broadcasting, contracts, http, pagination, queue] = l13RealDeps minus l42RealDeps (content unchanged, cache is dropped not new). Model implements signatures verified in both trees — apiDelta accurate. migrationClass flip-only justified: Model has zero delegation to a maintained primitive (3125-line core), 'done' impossible. effort very-high sane given largest surface + DB regression risk. blockers CORRECTED: set unchanged (== l13RealDeps, all 10 must be L13-shaped before flip, none 'done') but reordered/deduped for consistency; cache correctly excluded because L13 Database no longer depends on it (its only L42x use was Capsule\Manager's CacheManager, gone in L13).


Cookie · shim-symfony / effort medium

Map L13: Cookie Real deps (use): encryption, support Blockers: encryption, support Dep baru di L13: collections, contracts, macroable

API delta:

CookieJar core stays backward-compatible for the L42x call surface. L13 make/forever gain optional $raw and $sameSite and change $secure default false->null (inherit config); setDefaultPathAndDomain gains $secure,$sameSite; new methods expire(), flushQueuedCookies(). Queue storage changes flat name=>Cookie -> nested name=>path=>Cookie, so queued()/hasQueued()/unqueue() gain optional $path and getQueuedCookies() now Arr::flatten's the nested map (was a plain return of the flat array in L42x). L13 CookieJar implements Contracts\Cookie\QueueingFactory and uses Macroable + InteractsWithTime (availableAt() replaces L42x's raw time()+minutes*60). Biggest structural change: L42x Guard.php and Queue.php (both HttpKernelInterface StackPHP decorators, already modernized to typed handle():Response + MAIN_REQUEST) are REPLACED by L13 Middleware/EncryptCookies.php and Middleware/AddQueuedCookiesToResponse.php (handle($request, Closure $next) pipeline middleware). L13 EncryptCookies adds CookieValuePrefix (HMAC-sha1 'v2'-tagged) validation on read, key rotation via getAllKeys(), disableFor/except/serialized/neverEncrypt/flushState, and depends on the Encryption CONTRACT (Contracts\Encryption\Encrypter) rather than L42x's concrete Illuminate\Encryption\Encrypter. New CookieValuePrefix helper class. L42x Guard used $encrypter->encrypt($value) with no prefix and duplicated cookies by hand; L13 uses $cookie->withValue().

Public API (app-facing):

  • CookieJar::make($name,$value,$minutes=0,$path=null,$domain=null,$secure=false,$httpOnly=true)
  • CookieJar::forever($name,$value,$path,$domain,$secure,$httpOnly)
  • CookieJar::forget($name,$path,$domain)
  • CookieJar::queue(...) (accepts a Symfony Cookie or make() args)
  • CookieJar::queued($key,$default=null) (L42x: 2 args, no $path)
  • CookieJar::hasQueued($key) (L42x: 1 arg)
  • CookieJar::unqueue($name) (L42x: 1 arg)
  • CookieJar::setDefaultPathAndDomain($path,$domain) (L42x: 2 args, no secure/sameSite)
  • CookieJar::getQueuedCookies() (L42x: returns flat name=>Cookie map)
  • Cookie facade (accessor 'cookie')
  • Guard (HttpKernelInterface StackPHP decorator; encrypt/decrypt request+response cookies)
  • Queue (HttpKernelInterface StackPHP decorator; flushes queued cookies onto response)

Risks:

  • ⚠️ Guard/Queue are HttpKernelInterface StackPHP decorators wired in Foundation/Application.php; converting to L13 Middleware/* requires the HTTP kernel middleware pipeline to be L13-shaped first (flip-time coupling, not incremental).
  • ⚠️ L13 EncryptCookies HMAC-prefixes cookie values (CookieValuePrefix 'v2') and validates on read; L42x Guard does raw encrypt/decrypt with no prefix. At the flip, pre-existing encrypted cookies from the old app fail prefix validation and are nulled out -- users get logged out / lose cookie state once. Expected and acceptable.
  • ⚠️ Queue storage shape changes flat name=>Cookie -> nested name=>path=>Cookie; any app code reading CookieJar internals or relying on getQueuedCookies() being the raw flat map (it now returns Arr::flatten of the nested map) could shift.
  • ⚠️ New dep on the Encryption CONTRACT: encryption must expose Contracts\Encryption\Encrypter (getKey/getAllKeys for key rotation) before EncryptCookies can be ported; encryption is no longer a direct composer dep of Cookie.
  • ⚠️ make()/forever() $secure default changes false->null (inherit config) and sameSite defaults to 'lax'; cookies that were non-secure/no-samesite in 4.2 may behave differently under stricter browser rules.

Notes: CORRECTED. realDeps confirmed by grep = encryption+support (matches composer.json illuminate/encryption + illuminate/support 4.2.*). L42x has NO Contracts package, so contracts (Contracts\Cookie\QueueingFactory, Contracts\Encryption\Encrypter/DecryptException) is genuinely new L13 coupling; collections (Arr::flatten/Arr::last/Arr::wrap) and macroable (Traits\Macroable) are also new. l13RealDeps=collections+contracts+macroable+support confirmed against L13 composer.json exactly (grep of use-statements only surfaces Contracts/Support/Cookie because Arr and Macroable live under the Illuminate\Support namespace though they ship as the collections/macroable packages). encryption is DROPPED as a direct Cookie dep in L13 -- it survives only via the Encrypter contract injected into EncryptCookies. migrationClass shim-symfony is justified: L42x CookieJar directly new Symfony\Component\HttpFoundation\Cookie(...) (CookieJar.php:46) and L13 does the same (CookieJar.php:70) -- the maintained Symfony primitive is the engine on both sides, so no engine swap at flip; only API/dep alignment plus the decorator->middleware reshape. Not "done" (signatures + deps differ, decorators must be rewritten as middleware), not "evaporate" (the Symfony wrapper stays). effort raised low->medium: CookieJar itself barely changes, but the flip requires rewriting 2 StackPHP decorators into 2 pipeline middleware, introducing CookieValuePrefix, and rebinding onto the Encryption contract -- real structural work, above trivial. Support helpers inside L42x CookieJar: array_get, head (-> L13 Arr::last, value). Wiring at Foundation/Application.php pushes Cookie\Guard + Cookie\Queue onto the StackPHP kernel-decorator stack; in L13 this becomes EncryptCookies + AddQueuedCookiesToResponse in the HTTP kernel middleware pipeline. Stable consumers across the flip: Auth\Guard (setCookieJar/queue/forever/forget), Session\CookieSessionHandler, Cookie facade. The record's original publicApi listed L13-shaped signatures for L42x methods; corrected to the actual L42x 4.2 signatures (queued 2-arg, hasQueued/unqueue 1-arg, setDefaultPathAndDomain 2-arg).

Verify note (koreksi adversarial): Verified against source. realDeps confirmed via grep (encryption, support) -- complete, no hallucinations/misses. l13RealDeps (collections, contracts, macroable, support) and newL13Deps confirmed against L13 composer.json at /tmp/laravel-framework (13.x). Read all L42x files (CookieJar.php, Guard.php, Queue.php, CookieServiceProvider.php, composer.json) and L13 files (CookieJar.php, Middleware/EncryptCookies.php, Middleware/AddQueuedCookiesToResponse.php, CookieValuePrefix.php, composer.json). Changes made: (1) corrected publicApi signatures to actual L42x 4.2 shapes (queued 2-arg, hasQueued/unqueue 1-arg, setDefaultPathAndDomain 2-arg) -- original listed L13-shaped $path args on the L42x surface; (2) raised effort low->medium given the decorator->middleware rewrite + CookieValuePrefix + Encryption-contract rebind. migrationClass shim-symfony CONFIRMED by reading CookieJar.php:46 (L42x) and :70 (L13) directly instantiating the Symfony Cookie primitive. blockers = realDeps (encryption, support), correct per schema. Everything else confirmed.


Encryption · shim-standalone / effort low

Map L13: Encryption Real deps (use): support Blockers: support, contracts Dep baru di L13: contracts

API delta:

Core algorithm is IDENTICAL between 4.2 and 13 (OpenSSL, JSON payload {iv,value,mac}, HMAC-SHA256, AES-256-CBC). L42x is a hand-ported native openssl_encrypt/openssl_decrypt implementation, so the wire format is compatible with L13's CBC ciphers. BUT the L42x class is a fully standalone reimplementation, not a delegation to a maintained primitive: it implements NO interfaces and pulls in ZERO Illuminate deps in the class body (only two DEAD Symfony imports). Differences added by L13: (1) implements Contracts\Encryption\Encrypter + StringEncrypter interfaces (L42x implements none); (2) exceptions live in Illuminate\Contracts\Encryption (DecryptException/EncryptException) — L42x has concrete Illuminate\Encryption\DecryptException (extends RuntimeException) plus an unused InvalidKeyException file; (3) L13 adds a distinct EncryptException on encrypt failure (L42x throws \RuntimeException); (4) AEAD (aes-128/256-gcm) with a 'tag' field; (5) key rotation previousKeys()/getAllKeys()/getPreviousKeys(); (6) generateKey($cipher), appearsEncrypted($value); (7) #[\SensitiveParameter] on key/value args; (8) DEFAULT CIPHER: L42x ctor defaults to AES-256-CBC, L13 defaults to aes-128-cbc; (9) L42x has setKey() that L13 dropped; (10) L42x supported() is PRIVATE static, L13's is public static. ServiceProvider: L42x uses the REMOVED bindShared('encrypter') passing only app.key with no cipher; L13 singleton parses base64: prefix, wires cipher + previous_keys config, and sets SerializableClosure secret key.

Public API (app-facing):

  • __construct($key, $cipher='AES-256-CBC')
  • encrypt($value, $serialize=true)
  • encryptString($value)
  • decrypt($payload, $unserialize=true)
  • decryptString($payload)
  • getKey()
  • setKey($key)

Risks:

  • ⚠️ migrationClass is NOT 'done': the L42x Encrypter implements no interfaces, throws \RuntimeException/concrete DecryptException (not the Contracts exceptions), uses the removed bindShared() in its provider, and lacks previousKeys/generateKey/AEAD. It is a standalone custom primitive to be REPLACED by upstream illuminate/encryption once Contracts exists — real swap work, not zero.
  • ⚠️ The Contracts component DOES NOT EXIST in the L42x tree (no src/Illuminate/Contracts dir at all). The prior record's verifyNote claiming 'L13 Contracts\Encryption dir confirmed present with 4 files' is FALSE. Contracts must be INTRODUCED and be L13-shaped before the L13 Encrypter can compile — this is the hard gate.
  • ⚠️ Default cipher mismatch: L42x class defaults to AES-256-CBC and its provider passes NO cipher (relies on the 256 default + a 32-byte key); L13 provider reads config['cipher'] (class default aes-128-cbc). L13 app config MUST set cipher=AES-256-CBC (or a matching 32-byte key) so existing cookies/cache stay decryptable.
  • ⚠️ Exception namespace change: consumers catching Illuminate\Encryption\DecryptException must move to Illuminate\Contracts\Encryption\DecryptException. In-framework, Cookie\Guard imports the concrete Illuminate\Encryption\DecryptException and needs updating.
  • ⚠️ More in-framework consumers of the concrete Encrypter than the prior record listed: Cookie\Guard, Cache\DatabaseStore, Queue\Queue, Queue\Connectors\IronConnector, Queue\IlluminateQueueClosure, the Crypt facade @see, and Foundation\Application alias 'encrypter' => Illuminate\Encryption\Encrypter. All reference the concrete FQCN and would flip to the illuminate/encryption package class.
  • ⚠️ L42x Encrypter.php carries two DEAD Symfony imports (Security\Core\Util\StringUtils, SecureRandom) — no body usage (confirmed). symfony/security-core ~6.4 in this component's composer.json is an unnecessary dep to drop on flip.
  • ⚠️ setKey() is L42x public API that L13 removed; grep before deleting (only in-tree setKey references are outside Encryption, verify none are the Encrypter's).

Notes: Wire format and MAC scheme match L13's CBC path 1:1, so AES-256-CBC blobs are cross-compatible — that is the valuable part and keeps effort LOW. But this is NOT already-done work: the L42x class is a self-contained reimplementation (no Illuminate deps in the class body; realDeps=[support] comes solely from the ServiceProvider's use of Illuminate\Support\ServiceProvider). Migration = drop the L42x custom class + provider in favor of the upstream illuminate/encryption package, which is gated on: (1) Contracts being INTRODUCED and L13-shaped (it does not exist in the tree yet — the sole NEW dep), (2) config carrying cipher=AES-256-CBC + optional previous_keys, (3) updating the ServiceProvider off bindShared(), (4) updating consumers (esp. Cookie\Guard's concrete DecryptException import) for the exception namespace move. No mcrypt anywhere (grep confirmed). One L42x test (tests/Encryption/EncrypterTest.php) covers round-trip + custom cipher + invalid-payload; superseded by L13's package tests.

Verify note (koreksi adversarial): CHANGED 4 things after reading both files end-to-end. (1) migrationClass 'done' -> 'shim-standalone': L42x Encrypter implements no interfaces, throws non-Contracts exceptions, uses removed bindShared(), and depends on a Contracts component that does not exist — it does NOT delegate to a maintained primitive, so 'done' is unjustified; it's a standalone class to be replaced. (2) Removed 'static supported($key,$cipher)' from publicApi — it is PRIVATE static (Encrypter.php:46), not public API. (3) blockers 'support' -> ['support','contracts']: the L13 form cannot compile without Contracts, which is absent from the tree. (4) Corrected the prior verifyNote's false claim: there is NO src/Illuminate/Contracts dir at all (ls empty / 'no Contracts under Illuminate'), so 'Contracts\Encryption dir confirmed present with 4 files' was fabricated. realDeps=[support] confirmed by the required grep. Also noted more consumers (Queue.php, IronConnector, IlluminateQueueClosure, Crypt facade) and the unused InvalidKeyException.php the prior record missed. effort 'low' retained (wire-compatible, small surface).


Events · reshape-callsites / effort medium

Map L13: Events (illuminate/events) Real deps (use): container, support Blockers: container, support Dep baru di L13: bus, collections, contracts, macroable, queue, reflection

API delta:

Same component name (illuminate/events, class Illuminate\Events\Dispatcher) but the class grew ~4x (7.5KB -> 28KB) and the app-facing API was renamed and expanded. Verified against L13.x source (/tmp/laravel-framework): fire($event,$payload,$halt) -> dispatch(...); queue($event,$payload) -> push($event,$payload); forgetQueued() -> forgetPushed(); firing() REMOVED entirely (0 matches in L13 Dispatcher.php); listen() drops the third $priority arg (signature is now listen($events,$listener=null)). New surface not in 4.2: push/flush deferred semantics, subscribe array-return maps, defer(), setQueueResolver(), setTransactionManagerResolver(), getRawListeners(), hasWildcardListeners(), NullDispatcher, QueuedClosure + queueable(). L13 class now implements DispatcherContract and mixes in Macroable, ReadsClassAttributes, ReflectsClosures, ResolvesQueueRoutes (record omitted ResolvesQueueRoutes). Understands ShouldQueue/ShouldBroadcast/ShouldDispatchAfterCommit/ShouldBeEncrypted plus queue Attributes (Timeout/Tries/Backoff etc.) — none exist in 4.2. Behavioral shift: 4.2 dispatches STRING event names ('auth.login','illuminate.query') with an array payload; L13 idiom is OBJECT events (class name = key) with the object as sole payload, plus wildcard listeners receiving ($eventName,$payload).

Public API (app-facing):

  • listen($events,$listener,$priority)
  • fire($event,$payload,$halt)
  • until($event,$payload)
  • queue($event,$payload)
  • flush($event)
  • firing()
  • subscribe($subscriber)
  • hasListeners($eventName)
  • getListeners($eventName)
  • makeListener($listener)
  • createClassListener($listener)
  • forget($event)
  • forgetQueued()

Risks:

  • ⚠️ fire() -> dispatch() rename: exactly 21 internal framework ->fire() callsites confirmed across src/Illuminate (View/Factory composing+creating, Database/Connection illuminate.query + connection.* events, Auth/Guard auth.attempt/login/logout, Log/Writer illuminate.log, Routing/Router router.matched, Queue/Worker illuminate.queue.failed+stopping, Foundation/Application provider-registered + locale.changed, Cache ClearCommand cache:clearing/cleared, Mail/Mailer mailer.sending) — every one must become dispatch(); app-space Event::fire()/Event::listen() callsites need the same sweep.
  • ⚠️ firing() removed: any app code inspecting the currently-firing event has no direct L13 equivalent and must be re-architected (pass the event object instead).
  • ⚠️ Priority arg dropped from listen(): any listener registered with a non-zero $priority silently loses ordering guarantees.
  • ⚠️ String-keyed events still work as arbitrary string keys in L13, so a lazy migration can keep string names + dispatch() without converting to event objects. But L13 wildcard listeners receive ($eventName, $payload), NOT the spread array payload — mixing L42x array-payload listeners with L13 wildcard semantics is a trap.
  • ⚠️ queue()/flush() semantics changed: L42x queue() defers via a synthetic '_queue' listener that re-fires in-process; L13 push()/flush() use a real deferred store. forgetQueued() -> forgetPushed() rename.
  • ⚠️ L13 Dispatcher's queued-listener/after-commit/broadcast features hard-depend on Bus, Queue and a db.transactions resolver (EventServiceProvider wires setQueueResolver + setTransactionManagerResolver). These gate the FINAL engine binary flip, not the callsite reshape — those subsystems must exist at flip time or the enriched Dispatcher construction breaks. (They are NOT realDeps of the L42x source, so they are not blockers of this component's reshape.)

Notes: L42x Events is a self-contained pub/sub engine: realDeps are ONLY container (IoC for class-listener resolution) + support (Str::contains, str_is, ends_with, last helpers) — confirmed by grep 'use Illuminate\' = Container, Support and composer require illuminate/container + illuminate/support 4.2.*. It has NO queue/bus/broadcast coupling; the 'queue' method is a same-process deferred-fire trick, not a real job queue. L13 turns Events into a heavier hub (Bus DebounceLock/UniqueLock, Queue ShouldQueue + Attributes + CallQueuedListener/InvokeQueuedClosure/QueuedClosure, Contracts, Broadcasting, transaction-aware after-commit dispatch, ReflectsClosures for queueable() typed listeners). Migration is two things: (1) trivial to keep the pub/sub core working, but (2) the queued-listener/after-commit/broadcast features are new and only light up once Bus+Queue are L13-shaped. Plan: reshape all ->fire() callsites to ->dispatch(), rename queue()->push()/forgetQueued()->forgetPushed() during the incremental phase (mechanical), keep string event names (L13 tolerates them), drop firing() usages, then flip the engine binary at the end alongside Bus/Queue. Per MIGRATION-ROADMAP.md the Events row is class=reshape, effort=medium, blocker=SCC-1 (its realDeps container+support live in SCC-1 and are broken via Contracts; neither is 'done'). CORRECTIONS from the reviewed record: effort high->medium (aligns with the authoritative roadmap and sibling reshape components view/cache/session; core is trivial, callsite sweep is mechanical, heavy effort lands with Bus/Queue themselves); blockers pruned from [container,support,bus,queue,contracts] to [container,support] because per the schema definition blockers = realDeps-not-yet-done, and bus/queue/contracts are not L42x realDeps (they gate the later binary flip, not this reshape).

Verify note (koreksi adversarial): Verified against real source. L42x realDeps [container,support] confirmed via grep 'use Illuminate\[A-Za-z]+' over src/Illuminate/Events (exactly Container, Support) and composer.json require — no hallucinated or missing entries. L42x publicApi confirmed by reading Dispatcher.php (all 13 methods present, incl. fire/queue/firing/forgetQueued). L13 claims verified against /tmp/laravel-framework (13.x): composer require = bus,collections,container,contracts,macroable,reflection,support; Dispatcher use-statements = bus,container,contracts,queue,support (matches l13RealDeps); newL13Deps set {bus,collections,contracts,macroable,queue,reflection} is set-correct; dispatch/push/forgetPushed/defer/setQueueResolver/setTransactionManagerResolver present and firing() removed. 21 internal ->fire() callsites confirmed by grep. migrationClass reshape-callsites confirmed (roadmap legend 'reshape' = bentuk ulang call-site, flip mesin di akhir; not 'done' — source fork with no delegation to a maintained primitive). CHANGES: (1) effort high->medium to match the authoritative MIGRATION-ROADMAP.md rating and sibling reshape components; (2) blockers [container,support,bus,queue,contracts] -> [container,support] because the schema defines blockers as realDeps-not-yet-done and bus/queue/contracts are L13-only deps absent from the L42x import set (the record itself concedes they only gate the final binary flip). All other fields confirmed accurate.


Filesystem · flip-only / effort low

Map L13: Filesystem Real deps (use): support Blockers: support Dep baru di L13: contracts

API delta:

L13 native Filesystem is a strict SUPERSET of the L42x class: every L42x public method survives with a backward-compatible signature (exists/get/getRequire/requireOnce/put/prepend/append/delete/move/copy/name/extension/type/size/lastModified/isDirectory/isWritable/isFile/glob/files/allFiles/directories/makeDirectory/copyDirectory/deleteDirectory/cleanDirectory). Additive-only for existing callers: get()/append() gain an optional $lock (default preserves old behavior) [NOTE: put() ALREADY had $lock=false in L42x, so no change there]; getRequire()/requireOnce() gain optional array $data=[]; files($directory, $hidden=false, $depth=0), allFiles($directory, $hidden=false), directories($directory, $depth=0) widen with optional trailing args. New methods added (missing, json, sharedGet, lines, hash, replace, replaceInFile, chmod, link/relativeLink, basename, dirname, guessExtension, mimeType, isReadable, isEmptyDirectory, hasSameHash, ensureDirectoryExists, moveDirectory, allDirectories, deleteDirectories) plus Macroable+Conditionable traits. ONE breaking move: FileNotFoundException relocated from Illuminate\Filesystem\FileNotFoundException to Illuminate\Contracts\Filesystem\FileNotFoundException (native class now imports it from contracts). Separately, L13 grows an entirely NEW Storage/flysystem layer alongside this class (FilesystemManager, FilesystemAdapter, LocalFilesystemAdapter, AwsS3V3Adapter, ReadThrough*, ServeFile/ReceiveFile) with no 4.2 counterpart — that layer is the 'introduce' surface, not this native helper. Service provider changed bindShared('files') -> singleton('files') for the same native binding, and now also registers 'filesystem'/'filesystem.disk'/'filesystem.cloud' + serveFiles() routes.

Public API (app-facing):

  • Filesystem::exists
  • Filesystem::get
  • Filesystem::getRequire
  • Filesystem::requireOnce
  • Filesystem::put
  • Filesystem::prepend
  • Filesystem::append
  • Filesystem::delete
  • Filesystem::move
  • Filesystem::copy
  • Filesystem::name
  • Filesystem::extension
  • Filesystem::type
  • Filesystem::size
  • Filesystem::lastModified
  • Filesystem::isDirectory
  • Filesystem::isWritable
  • Filesystem::isFile
  • Filesystem::glob
  • Filesystem::files
  • Filesystem::allFiles
  • Filesystem::directories
  • Filesystem::makeDirectory
  • Filesystem::copyDirectory
  • Filesystem::deleteDirectory
  • Filesystem::cleanDirectory
  • FileNotFoundException

Risks:

  • ⚠️ FileNotFoundException namespace moves to Illuminate\Contracts\Filesystem — any code (framework-internal here: no app-space callers found) that references or catches Illuminate\Filesystem\FileNotFoundException must be repointed; a class_alias can bridge during the flip.
  • ⚠️ This class is a foundational dependency consumed by many other Illuminate components in this repo (View, Config, Cache FileStore, Database Migrator/MigrationCreator, Translation loader, Foundation publishers/ProviderRepository/Composer). It cannot move ahead of them piecemeal — it flips together with the core.
  • ⚠️ L13 pulls in league/flysystem (+aws-s3-v3, ftp, sftp) and symfony/mime|filesystem for the new Storage layer; those are new composer requirements at flip (mostly 'suggest', not hard-require, in the native filesystem composer.json), but only for the driver-based Storage abstraction, not for the native File helper itself.
  • ⚠️ Signature widening (files/allFiles/directories/get/append/getRequire/requireOnce) is additive/backward-compatible, but any subclass or Mockery expectation asserting exact arg counts could break.

Notes: L42x Filesystem = the native local-filesystem helper bound as app('files') / the File facade — a thin wrapper over PHP fs functions + Symfony Finder, sole realDep illuminate/support (ServiceProvider). L13 keeps the SAME class name and role, so this is pure framework core that rides the final flip: no incremental swap, no app-space reshaping needed (this monolith has zero app-space Storage:: usage; all consumers are other Illuminate components). Treat as flip-only. Effort low: the class is essentially forward-compatible; the only real work is (a) repointing FileNotFoundException imports to Illuminate\Contracts\Filesystem (optionally via class_alias shim) and (b) matching the singleton('files') binding. CORRECTED l13RealDeps: the NATIVE Filesystem.php class in L13 imports ONLY contracts (FileNotFoundException) + support (LazyCollection, Conditionable, Macroable) — NOT container/http/image. Those three come exclusively from the SEPARATE, brand-new Storage/flysystem stack (FilesystemAdapter pulls container/http/image; ServeFile/ReceiveFile/provider pull http), which is its own 'introduce' concern under a Storage/FilesystemManager component and is NOT part of porting this native helper. So on the native path the only genuinely new dep is contracts (from the FileNotFoundException move).

Verify note (koreksi adversarial): Corrected two fields. (1) l13RealDeps was [container,contracts,http,image,support] — that is the whole-DIRECTORY grep, which conflates the native class with the new Storage layer. Read /home/agis/www/framework/src/Illuminate/Filesystem/Filesystem.php: the native class imports only Illuminate\Contracts\Filesystem\FileNotFoundException + Illuminate\Support\{LazyCollection,Traits\Conditionable,Traits\Macroable}. grep -rl showed container/http/image are pulled ONLY by FilesystemAdapter.php / ServeFile.php / ReceiveFile.php / FilesystemServiceProvider.php (the new driver stack). So l13RealDeps -> [contracts,support] and newL13Deps -> [contracts] for the native helper. This is exactly consistent with the record's own thesis that the Storage layer is a separate 'introduce'. (2) apiDelta claimed put() gains an optional $lock — but L42x put() already has $lock=false (line 68); the actually-new lock arg is on get()/append(). Fixed the apiDelta text. Everything else confirmed: realDeps=[support] (grep returns only Illuminate\Support), flip-only justified (native class forward-compatible, all consumers are other Illuminate components, no app-space Storage::), effort=low sane, blockers=[support] correct (support not 'done'). composer.json confirms http/image are only 'suggest', reinforcing they're not native-helper deps.


Redis · flip-only / effort low

Map L13: Redis Real deps (use): support Blockers: support, contracts, collections, macroable Dep baru di L13: contracts, collections, macroable

API delta:

Near-total rewrite. L42x is one flat Database class wrapping Predis\Client directly (connection/command/__call, cluster branch in constructor via array_except). L13 replaces it with RedisManager implements Contracts\Redis\Factory plus a Connections/ hierarchy (abstract Connection + PhpRedis/Predis + cluster variants + PacksPhpRedisValues), Connectors/ (PhpRedis, Predis), Events/ (CommandExecuted, CommandFailed) and Limiters/ (Concurrency/Duration builders). command() survives on Connection; Manager adds resolve, extend (via RebindsCallbacksToSelf), enableEvents/disableEvents, purge, setDriver, connections(), match-based driver selection (phpredis default vs predis), enum_value name handling. L42x provider used $this->app->bindShared('redis', new Database(config)) with defer=true; L13 provider implements DeferrableProvider and binds 'redis' (singleton -> RedisManager, client defaulting to 'phpredis' via Arr::pull) AND 'redis.connection' (-> $app['redis']->connection()).

Public API (app-facing):

  • Database::connection($name='default')
  • Database::command($method, array $parameters)
  • Database::__call($method, $parameters)
  • RedisServiceProvider::register() binds 'redis' via bindShared() to new Database(config['database.redis'])
  • Facade Redis -> 'redis'
  • Application.php:1144 aliases 'redis' => 'Illuminate\Redis\Database'

Risks:

  • ⚠️ Driver default flips from Predis (only option in 4.2) to phpredis (ext-redis) in L13 provider default (Arr::pull($config,'client','phpredis')); L42x wired purely on Predis. If ext-redis is not installed, config must set 'client' => 'predis' or connector() resolution returns null and connect fails.
  • ⚠️ Root composer pins predis ^2.4.1 while L42x Database still calls the 0.8-era new Client(array_values($servers)) / array_except cluster path - Predis 2.x aggregate/cluster client construction differs, so the legacy Database path is already fragile; irrelevant post-flip since it is deleted.
  • ⚠️ Internal consumers (Cache/RedisStore, Queue/RedisQueue+RedisJob+RedisConnector, Session) call the 4.2 command()/connection() surface - they must flip together with this component, not independently.
  • ⚠️ L13 fires Events\CommandExecuted/CommandFailed around commands; requires a bound 'events' dispatcher on connections when enableEvents() is on (configure() guards with $this->app->bound('events')).

Notes: Pure framework-core component with essentially no app-space custom logic - the whole thing is the vendored Illuminate package. L42x realDep is only illuminate/support (ServiceProvider + array_except global). Migration = delete the L42x flat Database class and drop in the L13 RedisManager + Connections/Connectors/Events/Limiters tree wholesale; do NOT port Database. L13 composer.json confirms the four deps: illuminate/collections, illuminate/contracts, illuminate/macroable, illuminate/support (all ^13.0). Its blockers (support/contracts/collections/macroable) are foundation pieces that land before the flip anyway, so effort here is low: swap the package, update the Application binding at Application.php:1144 and the provider to the L13 shape (bindShared->singleton, add 'redis.connection', DeferrableProvider), ensure config/database.php has a 'client' key and driver default. Coordinate the flip with the Redis consumers (Cache/Queue/Session) since they share the connection()/command() surface. This is a monorepo of the framework itself (no app/ dir), so no external call-site reshaping is needed beyond the internal consumers.

Verify note (koreksi adversarial): Confirmed. Re-ran the grep on L42x/src/Illuminate/Redis: sole dep is Illuminate\Support -> realDeps=[support] is complete with no hallucinated/missing entries. Read L42x Database.php (flat Predis wrapper, 98 lines) and RedisServiceProvider (uses bindShared, defer=true - not a plain closure bind; corrected the publicApi wording). Verified L13 against the real checkout at /home/agis/www/framework (v13.30.1, not /tmp): composer.json requires exactly collections/contracts/macroable/support ^13.0 -> l13RealDeps and newL13Deps confirmed; use-statement grep shows Support\Arr+Collection (=collections), Traits\Macroable (=macroable), Contracts* (=contracts). Read RedisManager.php and the L13 provider: provider binds 'redis'+'redis.connection', phpredis default via Arr::pull. Confirmed Application.php:1144 alias and Facade accessor 'redis'. migrationClass 'flip-only' justified: it is NOT 'done' (the file is not a delegating shim - it is the primitive being wholesale-replaced), no app call-sites to reshape (framework monorepo), so a package swap = flip. effort 'low' sane vs the large API delta because there is zero app-space logic to port and blockers are foundation deps landing first. blockers = union of realDep + newL13Deps = the set that must be L13-shaped before the flip; consistent. Only edits: clarified provider/publicApi wording (bindShared, redis.connection, Application:1144).


Console · flip-only / effort medium

Map L13: Console (native) Real deps (use): — Blockers: — Dep baru di L13: bus, cache, collections, container, contracts, filesystem, log, macroable, queue, reflection, support, view

API delta:

L42x Console is a thin Symfony Console (~6.4) wrapper: Application extends Symfony Application with static make()/start($app) factories and boot()/renderException(exceptionHandler) hooks; Command extends Symfony Command with getArguments()/getOptions() array-spec parameters and a fire() entrypoint (execute() casts fire() to int). Interactive I/O (confirm/ask/askWithCompletion/secret/choice) delegates to Symfony QuestionHelper; table() uses Symfony Table. L13 rewrites all of this: Command uses $signature string + Parser (or #[Signature] attribute), handle() instead of fire(), OutputStyle + laravel/prompts for interactive I/O, termwind components, Prohibitable/Signals/CommandMutex traits, fail()/ManuallyFailedException. Application constructor changes to (Container $laravel, Dispatcher $events, $version) [verified at framework/.../Application.php:69], implements the Kernel/Application contract, adds ContainerCommandLoader, addCommand(), output(). Whole subtree gains Attributes/Concerns/Contracts/Events/Scheduling/View directories. ConfirmableTrait signature stays compatible but the Command surface (getArguments/getOptions/fire) is the big break.

Public API (app-facing):

  • Application::make/start/boot/call/add/resolve/resolveCommands/setLaravel/setExceptionHandler/setAutoExit
  • Command::__construct/run/fire/call/callSilent/argument/option/confirm/ask/askWithCompletion/secret/choice/table/info/line/comment/question/error/getArguments/getOptions/getOutput/getLaravel/setLaravel
  • ConfirmableTrait::confirmToProceed

Risks:

  • ⚠️ fire() -> handle() rename: 48 fire() definitions across Auth/Cache/Database/Events/Foundation/Queue/Routing/Session/Workbench Console dirs must be renamed at flip (empirically counted)
  • ⚠️ getArguments()/getOptions() array-spec parameters -> $signature string parsing (Parser); silent behavior change if any command relies on the old array format
  • ⚠️ Application::make($app)/start($app) static factory and setExceptionHandler()/renderException() hooks removed in L13 (constructor now takes Container+Dispatcher+version, wired via Foundation\Console\Kernel) — bootstrap code must be reshaped
  • ⚠️ command.stub template ships the old fire()/getArguments idiom; any app scaffolding built from it produces L42x-shaped commands
  • ⚠️ interactive helpers (confirm/ask/askWithCompletion/secret/choice) reimplemented on laravel/prompts in L13 — output formatting and non-TTY fallback behavior differ

Notes: L42x version already partially modernized: composer requires symfony/console ~6.4 (not the original 4.2-era ~2.x) and code uses PHP 8 first-class callables ($this->addArgument(...)), #[\Override], typed returns (run():int, execute():mixed). So the Symfony layer is already current-ish; the gap is the Illuminate-idiom layer (signature parsing, prompts, handle()). Consumers in this repo all live inside other Illuminate components' /Console/ dirs (Auth, Cache, Database, Events, Foundation, Queue, Routing, Session, Workbench) plus Foundation/Console/stubs/command.stub — 48 fire() definitions and 39 extends Command classes across those components. There is essentially no true app-space code extending Console directly here; every extender is framework core that flips together. realDeps is EMPTY because no Console/.php contains any use Illuminate\... statement (Illuminate refs in Application.php/Command.php are docblock-only: \Illuminate\Foundation\Application, \Illuminate\Exception\Handler), and composer.json requires only php + symfony/console — so this node has no graph blockers and flips atomically with the framework swap. Effort is medium not low: app+framework commands must migrate fire()->handle() and getArguments()/getOptions()->$signature, and any code calling Application::make($app)/start($app) must move to the L13 three-arg constructor wired via Foundation\Console\Kernel.

Verify note (koreksi adversarial): Corrected l13RealDeps: original omitted collections, macroable, reflection, view — all four are HARD require entries in framework/.../Console/composer.json (illuminate/collections, /macroable, /reflection, /view ^13.0) and are used pervasively via Support/Contracts, so they belong in the real L13 dep set. Full L13 dep set is 12: the 6 hard composer requires (collections, contracts, macroable, reflection, support, view) plus 6 code-confirmed deps that composer lists only under suggest but that actually appear in use-statements in the Scheduling/GeneratorCommand subtrees (cache x3, filesystem x2, bus, cache, container, log, queue x1 each). newL13Deps == l13RealDeps because L42x realDeps is empty (verified: grep 'use Illuminate\[A-Za-z]+' over L42x Console returned nothing; composer.json requires only php + symfony/console ~6.4). realDeps EMPTY, blockers [] confirmed. migrationClass flip-only and effort medium confirmed accurate (not done — L42x is a Symfony wrapper fully rewritten in L13, not delegation to a maintained primitive). Also added askWithCompletion to risks list (present at Command.php:217) and made the fire()-count risk empirical.


Config · reshape-callsites / effort medium

Map L13: Config Real deps (use): filesystem, support Blockers: support Dep baru di L13: contracts, collections

API delta:

Repository shrank from a loader-driven, environment-aware, package-cascading resolver to a thin flat-array wrapper. Constructor changed from (LoaderInterface $loader, string $environment) to (array $items = []) — the single biggest breaking change: config is now PRE-LOADED into a flat array rather than lazy-loaded per group from disk. Repository no longer extends NamespacedItemResolver; it implements Contracts\Config\Repository and uses Macroable. REMOVED from public API: the whole loader plumbing (getLoader/setLoader/LoaderInterface/FileLoader), environment awareness (getEnvironment(), env-cascade config/{env}/{group}.php), package cascading (package(), afterLoading(), cascadePackage(), addNamespace/getNamespaces), namespaced '::' keys, hasGroup(). REMOVED components entirely: EnvironmentVariables + FileEnvironmentVariablesLoader + EnvironmentVariablesLoaderInterface (env loading replaced by vlucas/phpdotenv via Foundation\Bootstrap\LoadEnvironmentVariables). ADDED: typed getters string()/integer()/float()/boolean()/array()/collection() (throw InvalidArgumentException), getMany(), prepend(), push(), all(). Internals swapped array_get/array_set helpers for Arr::get/set/has. File I/O relocated to Foundation\Bootstrap\LoadConfiguration.

Public API (app-facing):

  • Repository::get($key, $default)
  • Repository::set($key, $value)
  • Repository::has($key)
  • Repository::hasGroup($key)
  • Repository::package($package, $hint, $namespace)
  • Repository::afterLoading($namespace, Closure)
  • Repository::addNamespace($namespace, $hint)
  • Repository::getNamespaces()
  • Repository::getLoader()/setLoader()
  • Repository::getEnvironment()
  • Repository ArrayAccess (offsetGet/Set/Exists/Unset)
  • FileLoader::load($env,$group,$namespace)
  • FileLoader::exists()
  • FileLoader::cascadePackage()
  • LoaderInterface
  • EnvironmentVariables::load()
  • FileEnvironmentVariablesLoader

Risks:

  • ⚠️ Constructor signature change (loader+env -> flat array) breaks any direct instantiation and any framework code passing a loader; real instantiation lives in Foundation/start.php:133 (new Config(new FileLoader(new Filesystem, path/config)@Application.php:992, env)), so it moves with the Foundation flip, not app code.
  • ⚠️ getEnvironment() is REMOVED from L13 Repository/contract but the dicoding app CALLS it in real callsites — Config::getEnvironment() appears in app/views/ui/commons/pixel.blade.php, sentry.blade.php, campaigns .../payment.blade.php, app/commands/ConfigureUserAbilitiesCommand.php and several tests. These break and must be reshaped to app()->environment() / App::environment(). This is why the migration is NOT flip-only.
  • ⚠️ App code using package-cascade config, '::' namespaced config keys, ->package(), ->afterLoading(), or ->hasGroup() has NO L13 equivalent and must be reshaped in app-space (packages register config via service providers / mergeConfigFrom in L13). No '::' keys were found in dicoding/app, but the getEnvironment reshape is confirmed real.
  • ⚠️ Old .env.php EnvironmentVariables loader is gone — any reliance on PHP-array env files must migrate to vlucas/phpdotenv .env format.
  • ⚠️ Per-environment config/{env}/{group}.php cascade folders are removed in L13; env-specific values must move to env() reads inside config files.
  • ⚠️ app code that reads config via $config->get()/array access / config() helper is UNAFFECTED — that read surface is preserved; only the loader/env/package-cascade surface breaks.

Notes: reshape-callsites, not flip-only: the plain read surface (get/set/has/ArrayAccess/config()) survives identically, but getEnvironment() is removed AND actually used by dicoding app callsites (blades + a command), so those must be rewritten to app()->environment(). The engine underneath (loader, environment cascade, package cascade, .env.php) evaporates and is replaced by Foundation bootstrappers (LoadConfiguration + LoadEnvironmentVariables + phpdotenv). Config's own filesystem dependency disappears — file I/O is now the bootstrapper's job; so filesystem is NOT an L13 blocker. Config's only surviving structural blocker is support (Arr/Collection/Macroable, which in L13 physically live in illuminate/collections + illuminate/macroable, required via illuminate/collections). The real coordination is with the Foundation bootstrap layer, which owns instantiation and loading and moves in the final flip. Effort medium (not low) because of the constructor/loading-model change, the getEnvironment callsite reshape, and relocating env + package-cascade behavior into L13 idioms during the Foundation flip.

Verify note (koreksi adversarial): Corrected 2 fields. (1) l13RealDeps: dropped "support" -> ["contracts","collections"]. L13 Config composer.json requires ONLY illuminate/collections + illuminate/contracts (NOT illuminate/support); Arr/Collection physically live in Illuminate/Collections/, Macroable in illuminate/macroable (transitive via collections). The use Illuminate\Support\... statements are namespace aliases, not an illuminate/support package dep. (2) migrationClass: flip-only -> reshape-callsites, empirically justified: grep of dicoding/app found real callers of the REMOVED Config::getEnvironment() (pixel.blade.php, sentry.blade.php, payment.blade.php, ConfigureUserAbilitiesCommand.php + tests) that must be rewritten to app()->environment(); a true flip-only would leave zero callsites to change. CONFIRMED unchanged: realDeps grep = {Filesystem, Support} exactly matches ["filesystem","support"]; L42x new Config at start.php:133 and new FileLoader at Application.php:992 (the record's extra ":1002" cite is not a FileLoader line, only 992+the 1128 binding matter — noted, no schema impact); newL13Deps ["contracts","collections"] correct; blockers ["support"] correct (filesystem evaporates in L13 so it is not a blocker); effort medium sane given loading-model change + callsite reshape.


Exception · evaporate / effort low

Map L13: absorbed-into-Foundation (Illuminate\Foundation\Exceptions\Handler + Contracts\Debug\ExceptionHandler) Real deps (use): support Blockers: support Dep baru di L13: contracts, container, http, routing, session, validation, auth, cache, console, database, log

API delta:

Entire standalone illuminate/exception package is gone in L13. L42x's procedural machinery (set_error_handler / set_exception_handler / register_shutdown_function, ReflectionFunction-based typed App::error() closure stack, isFatal/handleShutdown, ResponsePreparerInterface-driven prepareResponse, and the two-displayer Plain/Whoops model behind ExceptionDisplayerInterface) is replaced by Illuminate\Foundation\Exceptions\Handler implementing Illuminate\Contracts\Debug\ExceptionHandler with a completely different surface: report(Throwable), shouldReport(Throwable), render($request, Throwable), renderForConsole($output, Throwable), plus reportable()/renderable() closure registration. The handler is registered by the Foundation HTTP/Console kernels, not by a dedicated ExceptionServiceProvider. Whoops is dropped entirely in favour of Symfony ErrorHandler/HtmlErrorRenderer + Foundation\Exceptions\Renderer debug page.

Public API (app-facing):

  • Handler::register($environment)
  • Handler::handleError()
  • Handler::handleException()
  • Handler::handleUncaughtException()
  • Handler::handleShutdown()
  • Handler::handleConsole()
  • Handler::error(Closure)
  • Handler::pushError(Closure)
  • Handler::setDebug()
  • ExceptionDisplayerInterface::display(Exception)
  • PlainDisplayer
  • WhoopsDisplayer
  • ExceptionServiceProvider (binds app['exception'], app['exception.plain'], app['exception.debug'], app['whoops'])

Risks:

  • ⚠️ Any app-space custom handlers registered via App::error(Closure) / $app['exception']->error() rely on the L42x reflection-typed closure stack; these would need rewriting as ->reportable()/->renderable() on the L13 Foundation Handler. This repo has NO app/ bootstrap/ start/ directories at all, so there is nothing to migrate; the only ->error() usages live in tests/Exception/HandlerTest.php, which die with the package.
  • ⚠️ Whoops (filp/whoops 1.1.*) debug pages disappear; anything asserting on Whoops/JsonResponseHandler output changes to Symfony rendering.
  • ⚠️ The 'exception.plain'/'exception.debug'/'whoops'/'exception' container bindings vanish; any code resolving these strings breaks and must target the ExceptionHandler contract.
  • ⚠️ L13 Handler drags in a large new dependency fan-out (auth, validation, session, routing, http, cache, console, database exceptions) — but only inside framework core, not app code, since this evaporates.

Notes: L42x illuminate/exception has exactly ONE real Illuminate dep: support (ServiceProvider base + Support\Contracts\ResponsePreparerInterface). Verified: grep -rhoE 'use Illuminate\[A-Za-z]+' yields only 'use Illuminate\Support'; the deeper grep resolves to Support\Contracts (ResponsePreparerInterface) and Support\ServiceProvider — nothing else. All other deps are external (filp/whoops 1.1, symfony/error-handler ~6.4, symfony/http-foundation ~6.4, symfony/http-kernel ~6.4 per composer.json). In L13 there is no src/Illuminate/Exception dir (confirmed absent); handling lives in Illuminate\Foundation\Exceptions\Handler behind Contracts\Debug\ExceptionHandler (report/shouldReport/render/renderForConsole confirmed present), wired by Foundation kernels. Framework core: does not port incrementally. Classify as evaporate. Blocker is only 'support' being L13-shaped, but it rides along with the Foundation flip; effort low because nothing app-space to extract or reshape.

Verify note (koreksi adversarial): Corrected the app-space claim. The prior record asserted "grep of app/ bootstrap/ start/ found zero App::error()" implying a clean search of existing dirs — but those directories DO NOT EXIST in this repo (l42x is framework-only, no app skeleton). The zero-usage conclusion still holds but for a different reason: no app code exists to hold custom handlers. Repo-wide grep found ->error(Closure) only in tests/Exception/HandlerTest.php, which dies with the package. All else confirmed empirically: realDeps=[support] complete (both use-statements resolve to Illuminate\Support), no hallucinations/omissions; L13 src/Illuminate/Exception ABSENT; replacement Foundation/Exceptions/Handler.php implements Contracts\Debug\ExceptionHandler with report/shouldReport/render/renderForConsole + reportable/renderable; migrationClass 'evaporate' justified (self-contained procedural package, delegates to no maintained primitive, superseded wholesale — correctly NOT 'done'); effort 'low' and blockers=[support] (sole realDep, not yet done) both correct.


Translation · flip-only / effort medium

Map L13: Translation Real deps (use): filesystem, support Blockers: filesystem, support, contracts, collections, macroable, reflection Dep baru di L13: contracts, collections, macroable, reflection

API delta:

L42x implements Symfony\Contracts\Translation\TranslatorInterface and exposes trans()/transChoice() (domain-based); L13 implements Illuminate\Contracts\Translation\Translator and drops those, adding string(), array(), hasForLocale(), has($key,$locale,$fallback), choice() with fallback, addLines(), addPath()/addJsonPath() + JSON translations, handleMissingKeysUsing(), determineLocalesUsing(), stringable()/ParsesStringables, setLoaded(). Local LoaderInterface becomes Contracts\Translation\Loader. Translator gains Macroable + ReflectsClosures traits and enum_value support. FileLoader gains multi-path array + JSON loading. Provider: bindShared->singleton, $defer flag->DeferrableProvider, path.lang array with framework default lang dir; new ArrayLoader, CreatesPotentiallyTranslatedStrings, PotentiallyTranslatedString classes. MessageSelector grows a full pluralization table (Interval/PluralizationRules folded in): 3.1K->11.6K in L13.30.1.

Public API (app-facing):

  • Translator::get($key,$replace,$locale)
  • Translator::has($key,$locale)
  • Translator::choice($key,$number,$replace,$locale)
  • Translator::trans() / transChoice() (L42x-only Symfony-style, domain-based)
  • Translator::addNamespace()
  • Translator::getLocale()/setLocale()/getFallback()/setFallback()/locale()
  • Translator::getSelector()/setSelector()
  • Translator::getLoader()
  • LoaderInterface::load()/addNamespace() (FileLoader)
  • TranslationServiceProvider bindings: 'translator', 'translation.loader'

Risks:

  • ⚠️ L42x has a hard dep on symfony/translation ~6.4 and implements Symfony's TranslatorInterface; L13 has NO Symfony dep and its own Contracts\Translation\Loader/Translator. Any core code type-hinting the Symfony TranslatorInterface or calling trans()/transChoice() with a $domain must be reshaped at flip. Verified: dicoding app/config have ZERO such references, so this risk is framework-internal only.
  • ⚠️ L42x LoaderInterface lives in the component; L13 moves it to Illuminate\Contracts\Translation\Loader (absent in l42x, which has no Contracts dir at all) - any custom loader type-hinting the old interface breaks. Verified: no app-space custom loaders in dicoding.
  • ⚠️ JSON translations (__('literal string')) do not exist in L42x; if any app code relies on JSON-key lookups it silently returns the key until the L13 FileLoader lands. dicoding uses 36 __() + 7 Lang::get() call-sites, all against group/key API.
  • ⚠️ Provider uses $app['config']['app.locale'] (l42x) vs $app->getLocale() (l13) and $app['path'].'/lang' vs $app['path.lang'] array with a framework-default lang dir - depends on Foundation/Application flip.
  • ⚠️ choice()/pluralization semantics differ: l42x delegates to Interval/PluralizationRules helpers; l13 MessageSelector is self-contained - verify plural strings render identically for id/en.

Notes: Core framework plumbing bound as 'translator'/'translation.loader' in the container. Not swappable to a standalone/Symfony lib incrementally - the Illuminate Translator API IS the product surface, and l42x here only wraps symfony/translation via implementing its interface (does not delegate to it). Correct move: flip-only - replace the whole component wholesale in the final flip once its blockers (support, filesystem, contracts, collections, macroable, reflection) are L13-shaped, then delete l42x LoaderInterface/Interval.php/PluralizationRules.php (folded into L13 MessageSelector). App-space impact is LOW: verified ~43 helper-style call-sites in dicoding (36 __() + 7 Lang::get()) that use the stable get/choice helper API; ZERO transChoice()/trans($domain)/Symfony-TranslatorInterface references anywhere in app/config, so the breaking surface is entirely l42x-local. contracts, collections, macroable, reflection do NOT exist as separate components in l42x (monolithic - Collection lives inside Support; no Contracts/Macroable/Reflection/Collections dirs), so they are introduce-blockers upstream, not fixable within Translation.

Verify note (koreksi adversarial): Re-verified against real checkouts. (1) realDeps {filesystem,support} CONFIRMED via grep of use-statements. (2) l13RealDeps CONFIRMED correct - measured against actual L13.30.1 composer.json at /home/agis/www/framework/src/Illuminate/Translation/composer.json (require = collections,contracts,filesystem,macroable,reflection,support). Note the L13 use-statements only surface 3 namespaces (contracts,filesystem,support) because the Illuminate\Support\ namespace masks physically-split packages: Collection/Arr live in Illuminate/Collections, Macroable in Illuminate/Macroable, ReflectsClosures/Reflector in Illuminate/Reflection - all confirmed on disk. newL13Deps, migrationClass=flip-only (wholesale replace, NOT done/delegation), effort=medium, and blockers (=all 6 l13RealDeps, none 'done') all CONFIRMED. CORRECTIONS: notes claimed '~60 call-sites' - actual is ~43 (36 __() + 7 Lang::get()); corrected. Fixed notes' misleading 'no Reflection dir' phrasing (l42x has Support/Reflector.php but no Reflection component dir - reworded to 'monolithic'). Added empirical zero-breaking-surface confirmation (0 transChoice/trans($domain)/Symfony-interface refs in dicoding app+config).


Log · reshape-callsites / effort medium

Map L13: Log (same Illuminate\Log namespace; Writer renamed to Logger, LogManager added as the 'log' binding) Real deps (use): events, support Blockers: support, events Dep baru di L13: container, contracts, database, queue

API delta:

L42x is a single Writer class: a thin Monolog-1.x wrapper where the framework/app wires handlers imperatively (useFiles/useDailyFiles/useErrorLog) via a log.setup closure resolved in LogServiceProvider, and getMonolog() exposes the raw Monolog instance. L13 replaces this with a config-driven LogManager (implements PSR-3 LoggerInterface) that reads config/logging.php to build channels (single/daily/stack/syslog/errorlog/slack/monolog/custom drivers), and exposes channel()/stack()/driver()/extend()/build()/getDefaultDriver() plus shared-context APIs (shareContext/withContext/withoutContext/flushSharedContext). Writer -> Logger (renamed): still the per-channel wrapper, but getMonolog() is renamed getLogger(); useFiles/useDailyFiles/useErrorLog handler-registration methods are GONE from the wrapper (their logic moved into LogManager driver methods); write() signature changed from variadic-first-arg-is-level to write($level,$message,$context=[]):void; log()/write()/writeLog() are PSR-3 with typed void returns; constructor now takes Psr\Log\LoggerInterface + Contracts\Events\Dispatcher (not concrete Illuminate\Events\Dispatcher), and the class uses the Conditionable trait. LogServiceProvider binds 'log' to a LogManager singleton ($this->app->singleton('log', fn($app) => new LogManager($app))) instead of eagerly constructing a Writer + resolving a log.setup closure; the provider is no longer deferred.

Public API (app-facing):

  • Writer::__construct(MonologLogger, ?Dispatcher)
  • Writer::useFiles($path,$level)
  • Writer::useDailyFiles($path,$days,$level)
  • Writer::useErrorLog($level,$messageType)
  • Writer::listen(Closure)
  • Writer::getMonolog()
  • Writer::write() (variadic: first arg is level)
  • Writer::__call (debug/info/notice/warning/error/critical/alert/emergency)
  • Writer::getEventDispatcher()/setEventDispatcher()

Risks:

  • ⚠️ Monolog major jump 1.6 -> 3.10: level constants became the Monolog\Level enum, handler/formatter APIs changed; any code calling getMonolog()/getLogger() and poking raw Monolog handlers breaks. Cannot be swapped incrementally - it is a breaking major.
  • ⚠️ Logging wiring moves from imperative (log.setup closure + Writer::useDailyFiles/useFiles/useErrorLog) to declarative config/logging.php channels driven by LogManager; introduces a hard dependency on Config being L13-shaped and requires authoring a logging.php channels array.
  • ⚠️ Internal blast radius is larger than a single import: Illuminate\Log\Writer is referenced in Foundation/Application.php:1138 (the 'log' => 'Illuminate\Log\Writer' container type-alias map, must repoint to Illuminate\Log\LogManager), Mail/Mailer.php (real type hints: use Illuminate\Log\Writer, protected Writer $logger, setLogger(Writer $logger) - all break on rename), Support/Facades/Log.php:4 (@see docblock), plus tests (Log/LogWriterTest.php, Mail/MailMailerTest.php). Every Writer type reference must be repointed to Logger.
  • ⚠️ getMonolog() -> getLogger() rename plus write() semantics change (L42x write() first variadic arg is the level; L13 write($level,$message,$context):void) break any dynamic/positional write() callers silently.
  • ⚠️ L13 LogManager pulls in container/contracts and (via Context repository + queue serialization/processors) database+queue - a heavier bind graph; the previously deferred provider became a plain non-deferred singleton.

Notes: Same Illuminate\Log namespace in both, so this is NOT evaporate/introduce - it is the SAME component reshaped, hence reshape-callsites (not 'done': the code does not delegate to a maintained primitive, it is a genuine reshape of the wrapper + wiring). realDeps for L42x = {events, support}: Writer imports Illuminate\Events\Dispatcher and Illuminate\Support\Contracts\{Jsonable,Arrayable}Interface; LogServiceProvider extends Illuminate\Support\ServiceProvider; composer require is illuminate/support + monolog ~1.6 (events is require-dev). Blockers therefore = support + events must be L13-shaped first. The L13 heavier deps (container/contracts/database/queue) are internal to the framework flip, not app-level blockers. Note events is DROPPED in L13 (concrete Illuminate\Events no longer imported; replaced by Illuminate\Contracts\Events\Dispatcher). conditionable is a real new coupling at the composer-package level but enters via Illuminate\Support\Traits\Conditionable, so under the use-statement rule it folds into 'support' and is NOT a separate namespace realDep - removed from newL13Deps for consistency with l13RealDeps. Effort is medium not high because the app-facing log METHODS (debug/info/error/... and the Log:: facade) stay unchanged PSR-3; only the wiring/setup layer, getMonolog()->getLogger() consumers, and the Writer->Logger type repoints churn.

Verify note (koreksi adversarial): Verified against L42x Writer.php/LogServiceProvider.php/composer.json and L13 (v13.30.1, /home/agis/www/framework) Logger.php/LogManager.php/LogServiceProvider.php/composer.json. realDeps {events,support} and l13RealDeps {container,contracts,database,queue,support} both confirmed by use-statement grep (authoritative). CORRECTIONS: (1) Removed the false 'exactly one use Illuminate\Log\Writer in the tree / lone import' claim - Writer is referenced in 6 internal sites (Application.php:1138 container map, Mailer.php type hints x3, Facades/Log.php docblock, 2 tests); expanded the blast-radius risk accordingly and added mail-coupling. (2) Dropped 'conditionable' from newL13Deps: it enters as Illuminate\Support\Traits\Conditionable so it folds under 'support' by the use-statement rule (kept as a note). (3) Tightened apiDelta with confirmed signatures (getMonolog->getLogger, write():void, non-deferred singleton, Contracts\Events\Dispatcher ctor). publicApi, migrationClass=reshape-callsites, effort=medium, and blockers=[support,events] all confirmed correct.


View · flip-only / effort high

Map L13: View Real deps (use): container, events, filesystem, support Blockers: container, events, filesystem, support Dep baru di L13: contracts, database, foundation, http

API delta:

Same spine, strictly additive in L13. L42x Factory is monolithic; L13 splits its section/component/event/stack/layout/loop/fragment/translation logic into Concerns\Manages* traits (ManagesComponents, ManagesEvents, ManagesFragments, ManagesLayouts, ManagesLoops, ManagesStacks, ManagesTranslations) — behavior preserved, callable methods (composer/creator/startSection/yieldContent/inject) largely identical but now trait-provided. New Factory API: file(), first(), renderWhen/renderUnless, flushState(). Renamed internals: flushSections->flushState, flushSectionsIfDoneRendering->flushStateIfDoneRendering (L42x names gone — verified: grep of L13 Factory returns no flushSections). Removed from L42x Factory: of()/name()/alias() named-view sugar (verified present in L42x lines 153/165/177, absent in L13). Big new subsystem absent in 4.2: Blade class components (Component, AnonymousComponent, DynamicComponent, ComponentAttributeBag, ComponentSlot, InvokableComponentVariable, AppendableAttributeValue) + ComponentTagCompiler (x-tag parsing, needs ext-tokenizer) + FileEngine + Middleware dir. BladeCompiler grew 17.5K->28.8K (verified via wc -c). View adds Macroable (via Support\Traits\Macroable) + fragments. Engine/Compiler dep typehints shifted from concrete Illuminate\Events\Dispatcher to Illuminate\Contracts\Events\Dispatcher; CompilerEngine now imports Illuminate\Database\Record(s)NotFoundException + Illuminate\Http\Exceptions\HttpResponseException; CompilesHelpers imports Illuminate\Foundation\Vite.

Public API (app-facing):

  • Factory::make($view, $data, $mergeData)
  • Factory::exists($view)
  • Factory::share($key, $value)
  • Factory::composer/creator/composers (moved into Concerns\ManagesEvents trait)
  • Factory::startSection/stopSection/appendSection/yieldSection/yieldContent/inject (moved into Concerns\ManagesLayouts+ManagesComponents traits)
  • Factory::addNamespace/addLocation/addExtension
  • Factory::getEngineResolver/getFinder/getDispatcher/getContainer
  • View::render()/with()/nest()/withErrors()/renderSections()
  • View implements ArrayAccess + __toString (L13: implements ArrayAccess, Htmlable, Stringable, ViewContract)
  • BladeCompiler (@if/@foreach/@yield/@section/@extends/@include directives, extend(), directive())
  • PhpEngine/CompilerEngine, EngineResolver::register/resolve
  • FileViewFinder/ViewFinderInterface::find/addLocation/addNamespace

Risks:

  • ⚠️ Blade compiled-cache is version-specific: L42x compiled .php in storage/framework/views must be flushed at flip or they'll break under L13 CompilerEngine.
  • ⚠️ App Blade templates using L4-era directive spellings or @section/@yield edge behaviors may render differently; any custom Blade::extend() macros from L4 must be re-registered against the L13 BladeCompiler API (extend/directive).
  • ⚠️ L13 View pulls in http (Http\Exceptions\HttpResponseException, in CompilerEngine) and foundation (Foundation\Vite, in CompilesHelpers) — @vite / response-abort inside views assume those components exist; if the app never adopted them it's inert, but the dep graph now hard-couples View to Foundation and Http.
  • ⚠️ ext-tokenizer required by ComponentTagCompiler; ensure PHP build has it (default-on but verify in the target PHP 8.3 image).
  • ⚠️ Factory internal method renames (flushSections -> flushState) will break any app/package code that reached into those non-facade methods directly.
  • ⚠️ ViewErrorBag/withErrors path depends on Session flash + Support ViewErrorBag being L13-shaped; verify error-bag sharing after Session migrates.

Notes: Core templating engine — not swappable for an external lib; it IS Blade. Classify flip-only: it ports/upgrades wholesale in the final flip once its four L42x deps (container, events, filesystem, support) are L13-shaped. The L42x public surface (make/share/composer/@directives/ArrayAccess View) is a subset of L13, so app code calling the Factory facade (View::make, view() helper, @section/@yield/@include Blade) keeps working. The heavy lift is not app call-sites (mostly compatible) but the engine internals: L13 Factory depends on the Concerns traits, a Component subsystem, ComponentTagCompiler (ext-tokenizer), and pulls in http/database/foundation(Vite) via use-statements — so you cannot cherry-pick a partial port. Effort high because BladeCompiler and the component compiler are large and tightly coupled to L13 Support (Collection, Str, HtmlString, Reflector) and Contracts. Note: L42x-only Html helpers (Form/Html) and any @-directives with changed semantics are the real callsite risk, tracked under the Html component, not here.

Verify note (koreksi adversarial): Corrected l13RealDeps and newL13Deps for methodology consistency. realDeps confirmed exact: grep of L42x View use-statements = container,events,filesystem,support (matches composer.json require @4.2). L13 framework is 13.30.1; its composer.json requires collections,conditionable,container,contracts,events,filesystem,macroable,reflection,support + php ^8.3 + ext-tokenizer. HOWEVER the record's stated authoritative methodology is the use-statement grep, and grep of L13 View use-statements yields only container,contracts,database,filesystem,foundation,http,support. The five deps (events, collections, conditionable, macroable, reflection) never appear as use Illuminate\{ns} in L13 View source — they are composer-require reached via Support/Contracts re-exports (Support\Traits\Macroable, Support\Collection, Contracts\Events\Dispatcher). Notably events is a HARD concrete dep in L42x (use Illuminate\Events\Dispatcher, Factory ctor) but in L13 it degrades to the CONTRACT (use Illuminate\Contracts\Events\Dispatcher) — so events is no longer a use-statement realDep in L13, only a composer-require. I trimmed l13RealDeps to the authoritative use-statement set and set newL13Deps = contracts,database,foundation,http (confirmed all four absent in L42x View; L42x's only Foundation reference is a docblock @param in ViewServiceProvider, not a use). migrationClass flip-only confirmed (core Blade, ports wholesale, L42x public surface is a subset — make/exists/share/addNamespace present in L13 Factory, composer/startSection/inject present via Concerns traits, View implements ArrayAccess in both). effort high confirmed (Concerns/Manages* traits, ComponentTagCompiler, FileEngine, component subsystem all new; BladeCompiler 17.5K->28.8K). blockers = realDeps (all four L42x deps must be L13-shaped before the flip) — internally consistent. All other fields accurate.


Workbench · evaporate / effort trivial

Map L13: removed Real deps (use): console, filesystem, support Blockers: —

API delta:

Entire component is gone in L13. In L4.2 it is a dev-only scaffolding tool: php artisan workbench vendor/package generates an in-repo package skeleton (PackageCreator writes composer.json, service provider, src/test/public dirs from stubs/). L13 has no illuminate/workbench, no workbench artisan command, no package.creator/command.workbench bindings, and no /workbench directory convention. The public surface (PackageCreator, Package, WorkbenchServiceProvider, WorkbenchMakeCommand) does not exist in L13.

Public API (app-facing):

  • PackageCreator::create(Package, path, plain)
  • PackageCreator::createWithResources(Package, path)
  • Package::__construct(vendor, name, author, email)
  • Package::getFullName()
  • WorkbenchServiceProvider::register()
  • Console\WorkbenchMakeCommand (php artisan workbench)
  • package.creator container binding
  • command.workbench container binding

Risks:

  • ⚠️ None functional: not registered in app/config, not referenced by any app code. Only self-reference in composer.json (illuminate/workbench: self.version) and Foundation's own legacy AutoloadCommand (findWorkbenches/getWorkbenchComposers reading path.base/workbench) and AssetPublishCommand (--bench option -> workbench/{bench}/public) reference the /workbench dir convention, both L4.2 dev tooling that also disappears in L13.
  • ⚠️ Cosmetic only: if any developer muscle-memory relies on php artisan workbench, the replacement workflow is a standalone Composer dev-dependency, not a core command.

Notes: Dev-only package scaffolder, DELETE at flip -- do not port. Deps console/filesystem/support are used purely to build the CLI command (Console/WorkbenchMakeCommand -> Illuminate\Console) and write stub files (PackageCreator/Starter -> Illuminate\Filesystem; WorkbenchServiceProvider -> Illuminate\Support); none carries over. The modern equivalent is a standalone Composer dev-dependency (orchestra/testbench + workbench cover this workflow), so no introduce work is needed in core. At flip: remove the illuminate/workbench: self.version line from root composer.json and drop the Foundation --bench/findWorkbenches/getWorkbenchComposers legacy code paths (AutoloadCommand, AssetPublishCommand) when Foundation is reshaped. CORRECTION: the prior claim that "L13 composer.json already requires orchestra/testbench-core ^11.0" is NOT verifiable in this repo -- this repo IS laravel/framework (self.version, no vendor/laravel/framework) and grep for testbench/orchestra in composer.json returns zero matches; treat testbench as the external ecosystem replacement, not a dependency already declared here.

Verify note (koreksi adversarial): Re-ran the grep: realDeps [console, filesystem, support] is complete and accurate (console from Console/WorkbenchMakeCommand, filesystem from PackageCreator+Starter, support from WorkbenchServiceProvider; Illuminate\Workbench self-ref and Symfony Finder correctly excluded). Read all 4 top-level classes (Package, PackageCreator, WorkbenchServiceProvider, Starter) + WorkbenchMakeCommand (command name confirmed 'workbench') + composer.json. Confirmed L13 Workbench dir ABSENT (ls) and no Illuminate\Workbench symbol referenced anywhere in src/ outside its own dir. Confirmed no app/config provider registration; only self-require in root composer.json and Foundation's own legacy AutoloadCommand/AssetPublishCommand reference the /workbench convention. migrationClass 'evaporate', effort 'trivial', blockers [] all justified (nothing must be L13-shaped before deleting a component that just disappears). CHANGED: removed the false/unverifiable claim from notes and verifyNote that L13 composer.json already requires orchestra/testbench-core ^11.0 -- grep for testbench/orchestra in this repo's composer.json returns 0 matches (this repo is laravel/framework itself using self.version, no vendor framework present).


Routing · reshape-callsites / effort very-high

Map L13: Illuminate\Routing (same component, deeply reshaped) Real deps (use): container, http, support, session, filesystem, events, console Blockers: container, http, support, session, contracts, collections, conditionable, macroable, pipeline, reflection Dep baru di L13: contracts, collections, conditionable, macroable, pipeline, reflection, database, cache, auth, redis

API delta:

Router stops being the HTTP kernel: L42x Router implements HttpKernelInterface, RouteFiltererInterface (verified line 14) with handle()+dispatch() running a global/route FILTER pipeline (before/after at callFilter lines 1033/1045; filter() 1182, whenRegex() 1226). L13 Router implements BindingRegistrar, RegistrarContract (verified line 38), uses Macroable/Tappable (lines 40/43), and dispatch runs through Illuminate\Pipeline (line 818) with a MIDDLEWARE stack (gatherRouteMiddleware line 832, MiddlewareNameResolver, SortedMiddleware, middlewarePriority, middlewareGroups). Request handling moves to a separate Illuminate\Http\Kernel. Removed: ControllerInspector + implicit RESTful controller()/controllers() (both present in L42x at lines 274/258; absent in L13), Generators/ + Console/ route generators, the entire filter engine + RouteFiltererInterface. Added: ResourceRegistrar, RouteRegistrar/RouteGroup/RouteAction/RouteBinding, ImplicitRouteBinding (Eloquent), ResponseFactory, CompiledRouteCollection (native route caching), events (RouteMatched/Routing/PreparingResponse). UrlGenerator (to/route/asset/action/secure/current) and Redirector (to/back/intended/guest/away/route/refresh) keep the same app-facing surface (verified both files).

Public API (app-facing):

  • Router::get/post/put/patch/delete/options/any/match
  • Router::group
  • Router::resource
  • Router::model/bind/pattern
  • Router::dispatch(Request)/dispatchToRoute(Request)
  • Router::filter/before/after/when/whenRegex (REMOVED in L13)
  • Router::controller/controllers (REMOVED in L13)
  • Router::current/currentRouteName/currentRouteAction/is/uses
  • Route::where/prefix/before/after/beforeFilters/afterFilters
  • UrlGenerator::to/secure/asset/route/action/full/current/previous
  • Redirector::to/back/intended/guest/away/route/refresh
  • RoutingServiceProvider binds router|url|redirect

Risks:

  • ⚠️ Filter system is fully removed in L13 - every app filter (filters.php, Route::filter, ->before/->after, when/whenRegex, route array 'before'=>) must be rewritten as middleware BEFORE the flip; there is no compatibility layer. This mandatory pre-flip app-space rewrite is why the class is reshape-callsites, not flip-only.
  • ⚠️ Router no longer implements HttpKernelInterface - any app/foundation code calling $router->handle()/dispatch() as the kernel breaks; request handling moves to Illuminate\Http\Kernel (separate component that must be introduced ahead of the flip)
  • ⚠️ Implicit RESTful auto-controllers (Route::controller/controllers + ControllerInspector, present in L42x at lines 274/258/ControllerInspector.php) removed - any use must be converted to explicit routes
  • ⚠️ Route model binding semantics changed: L13 ImplicitRouteBinding/RouteBinding couple to Illuminate\Database\Eloquent\ModelNotFoundException - bind()/model() call-sites and binder signatures differ
  • ⚠️ Route caching changes from L42x CachedRouting (app-space) to native CompiledRouteCollection - the custom cache layer evaporates
  • ⚠️ New compose-time coupling to contracts/collections/conditionable/macroable/pipeline/reflection (all in L13 illuminate/routing composer require) means those must be L13-shaped first or Routing won't compose. auth/cache/database/redis are optional runtime couplings via Throttle middleware + Eloquent binding (NOT in composer require, only in specific middleware/attribute files) - real code imports but not compose-time blockers.
  • ⚠️ L42x concrete illuminate/events dep is dropped: L13 Router depends only on Illuminate\Contracts\Events\Dispatcher (verified Router line 8/144), so events is absorbed into contracts and is not an L13 blocker.

Notes: Same component name in both, but internals are a near-total rewrite. Two hard breaks dominate: (1) filters -> middleware, and (2) Router-is-the-kernel -> Router is just a registrar/dispatcher with Illuminate\Http\Kernel owning request handling. The engine itself moves in the final flip, BUT the record's own risks establish mandatory app-space prerequisites (filter->middleware conversion of every filters.php/Route::filter/->before/->after call-site, plus introducing Http\Kernel) that must land BEFORE the flip - there is no L42x-API compatibility shim. That makes the honest class reshape-callsites (dominated by upstream call-site rewrites), NOT flip-only. UrlGenerator and Redirector are the stable parts - same public surface (verified), low risk. CachedRouting (L42x app-space route cache) evaporates into native CompiledRouteCollection. ControllerInspector/Generators/Console route-generation are dead code to delete, not port. Note events dropped as a hard dep (contract-only in L13); console is suggest-only in both.

Verify note (koreksi adversarial): Two corrections. (1) migrationClass flip-only -> reshape-callsites: the record's own risks/notes state the filter->middleware conversion and Http\Kernel introduction are mandatory PRE-flip app-space reshapes with no compat layer; that is not a clean flip. (2) blockers: dropped events - verified L13 Router imports only Illuminate\Contracts\Events\Dispatcher (Router.php line 8) and illuminate/events is NOT in L13 routing composer require (grep count 0), so events is absorbed into contracts, not a blocker; blockers now = the L13 composer-require compose-time deps (container/http/support/session/contracts/collections/conditionable/macroable/pipeline/reflection). Everything else confirmed empirically: L42x realDeps grep = console/container/events/filesystem/http/session/support (exact match, no hallucination/missing). L42x Router implements HttpKernelInterface,RouteFiltererInterface (line 14) with callFilter before/after (1033/1045); controller/controllers at 274/258. L13 Router implements BindingRegistrar,RegistrarContract (line 38) w/ Macroable/Tappable + Pipeline dispatch (818) + gatherRouteMiddleware (832). L13 composer require confirms collections/conditionable/macroable/pipeline/reflection/contracts. auth/cache/database/redis are import-level only (Throttle middleware + Eloquent binding), not composer require - kept in l13RealDeps as real imports but excluded from blockers. UrlGenerator/Redirector public surface stable across both (verified). effort very-high confirmed sane vs delta/dep-count.


Validation · flip-only / effort high

Map L13: Validation Real deps (use): container, database, support Blockers: container, database, support Dep baru di L13: contracts, foundation, http, translation

API delta:

L13 keeps the app-facing surface backward-compatible (make/passes/fails/messages/errors/sometimes/each) but ADDS a large new API: validate() (throws ValidationException, confirmed at Validator.php:597), safe() returning Illuminate\Support\ValidatedInput (:633), validated() (:647), stopOnFirstFailure() (:1379), fluent Rule:: static builder, object rule classes in Rules/ (30 classes incl. Unique/Exists/In/Enum/Password/File/Email/Dimensions/Date), NestedRules/ConditionalRules, UncompromisedVerifier (HaveIBeenPwned). Structural change confirmed: L42x is one 57.9K monolithic Validator with 44 inline protected validateXxx methods; L13 extracts all rule logic into Concerns traits (FormatsMessages 18.6K, ValidatesAttributes 84.9K, ReplacesAttributes 29.6K) + a Rules/ directory. Contract moved from Illuminate\Support\Contracts\MessageProviderInterface (L42x Validator.php:13/15) to Illuminate\Contracts\Validation\Validator (L13 implements ValidatorContract at :24). ServiceProvider bindShared->singleton and now registers UncompromisedVerifier via Http\Client\Factory (NotPwnedVerifier).

Public API (app-facing):

  • Factory::make(data, rules, messages, attributes)
  • Factory::extend/extendImplicit/replacer/resolver
  • Factory::setPresenceVerifier()
  • Validator::passes()
  • Validator::fails()
  • Validator::valid()/invalid()
  • Validator::messages()/errors()/getMessageBag()
  • Validator::sometimes()
  • Validator::each()
  • Validator::mergeRules()
  • Validator::addExtension()/addReplacer()
  • Validator::setPresenceVerifier()
  • PresenceVerifierInterface
  • DatabasePresenceVerifier

Risks:

  • ⚠️ L42x Validator is a single monolithic class with 44 inline validateXxx methods; L13 splits rule logic across Concerns traits (FormatsMessages/ValidatesAttributes/ReplacesAttributes) + 30 Rules/ classes, so any local patches to validation rules in the fork must be re-applied against the new trait/Rule layout, not the class body
  • ⚠️ L13 pulls in new hard deps not present in 4.2: illuminate/contracts, translation (as separate package), foundation, http, plus egulias/email-validator ^4, brick/math, symfony/mime, ext-filter/ext-mbstring - these must be available at flip. NOTE: collections/conditionable/macroable are new composer packages in L13's require but are consumed via Illuminate\Support* namespaces (Support\Collection, Support\Traits\Conditionable/Macroable), so they fold into the 'support' component at flip, not separate top-level deps
  • ⚠️ Contract identity changes: app code type-hinting the old Illuminate\Support\Contracts\MessageProviderInterface won't match L13's Illuminate\Contracts\Validation\Validator
  • ⚠️ UncompromisedVerifier/NotPwnedVerifier couples Validation to Http\Client\Factory (new runtime dependency) even if the 'uncompromised' rule is never used
  • ⚠️ PHP floor jumps 5.4 -> 8.3; the already-applied Symfony ~6.4 shim in L42x must reconcile with L13's Symfony ^7.4||^8 requirement (http-foundation + mime)
  • ⚠️ DatabasePresenceVerifier public API is stable but L13 adds a new DatabasePresenceVerifierInterface alongside the existing PresenceVerifierInterface (both confirmed present in L13 src)
  • ⚠️ database is require-DEV in L42x composer.json but appears in the authoritative use-statement grep (DatabasePresenceVerifier imports Illuminate\Database), so it is a real code-level dep for the presence-verifier path; in L13 it is a suggest, matching

Notes: This is the laravel/framework monorepo fork itself, not a consuming app - so there is no app-space call-site to reshape here; consumers live in downstream apps. The app-facing Validator::make/fails/messages surface is preserved in L13, so downstream callsites do not need pre-flip reshaping (unlike Session/Routing). The engine cannot be swapped incrementally: rule logic is inlined in one 57.9K class in 4.2 (44 validateXxx) and distributed across Concerns traits + 30 Rule classes in L13, so it is a wholesale replacement at flip. L42x already carries partial modernization (Symfony ~6.4, #[\Override], bindShared) indicating prior sync work. realDeps come straight from the L42x use-statement grep (container, database, support). L13 reference read from /home/agis/www/framework (v13.30.1). database is only needed for the presence verifier (unique/exists), matching L13 where it is a suggest, not a hard require.

Verify note (koreksi adversarial): CHANGED l13RealDeps and newL13Deps; everything else confirmed. (1) realDeps grep re-run: exactly container/database/support - complete, no hallucinations. (2) Public API + structural claims spot-checked against L13 src (/home/agis/www/framework, v13.30.1): validate()/safe()/validated()/stopOnFirstFailure() present at Validator.php:597/633/647/1379; implements ValidatorContract at :24; Concerns/{FormatsMessages,ValidatesAttributes,ReplacesAttributes} + 30 Rules/ classes confirmed; SP uses singleton + wires NotPwnedVerifier to Http\Client\Factory; L42x Validator implements Support\Contracts\MessageProviderInterface (:13/:15); L13 has BOTH PresenceVerifierInterface AND new DatabasePresenceVerifierInterface. (3) CORRECTION: original l13RealDeps listed collections/conditionable/macroable as separate top-level deps, but re-running the SAME use-statement methodology used for realDeps (grep 'use Illuminate\X') on L13 Validation yields container/contracts/database/foundation/http/support/translation (7), NOT 10 - collections is Support\Collection, conditionable/macroable are Support\Traits\*, all folding into 'support'; they are new composer packages but not separate top-level use-deps. Corrected newL13Deps accordingly to contracts/foundation/http/translation. migrationClass flip-only justified (framework fork, no app call-sites, app-facing API preserved, wholesale engine swap on foundational deps). effort high is sane (57.9K monolith->traits+30 Rules, PHP 5.4->8.3, contract-path change) but not very-high since app surface is backward-compatible. blockers == realDeps (container/database/support) is the correct conservative default.


Queue · flip-only / effort very-high

Map L13: Queue Real deps (use): cache, console, container, database, encryption, events, filesystem, http, redis, support Blockers: container, console, support, database, cache, events, redis Dep baru di L13: bus, contracts, foundation, log, pipeline

API delta:

Complete engine replacement. L42x: string/closure job dispatch via QueueInterface::push($job,$data,$queue); closures wrapped in IlluminateQueueClosure and encrypted with Illuminate\Encryption\Encrypter + Support\SerializableClosure (verified in Queue::createClosurePayload); Worker/Listener take positional args. L13: object-based job dispatch, every payload routes through 'Illuminate\Queue\CallQueuedHandler@call' (CallQueuedHandler.php confirmed present); adds dispatchAfterCommit, job batching (Bus), queue routing/forwarding (QueueRoutes), pause/resume/monitor, WorkerOptions/ListenerOptions value objects, WorkerStopReason, job UUIDs (ramsey/uuid), attribute-driven config (Attributes/: Backoff/Timeout/Tries/MaxExceptions), pipeline middleware, and a large Events/ + Middleware/ surface. Closure encryption now uses laravel/serializable-closure v2 (Laravel\SerializableClosure\SerializableClosure in CallQueuedClosure) + Contracts\Encryption\Encrypter, applied only when job instanceof ShouldBeEncrypted (verified Queue::jobShouldBeEncrypted). New drivers: Database (20KB), Null, Failover, Deferred/Background. QueueManager implements FactoryContract + MonitorContract (verified). Console gained batches/monitor/prune/pause/resume/clear commands.

Public API (app-facing):

  • QueueInterface::push($job, $data, $queue)
  • QueueInterface::pushRaw($payload, $queue, $options)
  • QueueInterface::later($delay, $job, $data, $queue)
  • QueueInterface::pop($queue)
  • QueueManager::connection($name)
  • QueueManager::extend($driver, $resolver)
  • QueueManager::addConnector($driver, $resolver)
  • QueueManager::looping/failing/stopping callbacks
  • Worker::pop() / Worker::daemon() / Worker::process()
  • Listener::listen()
  • Jobs\Job::fire/delete/release/attempts/getRawBody
  • Queue::createPayload() closure-serialization (IlluminateQueueClosure via Encrypter+SerializableClosure)

Risks:

  • ⚠️ Public dispatch API is incompatible: L42x QueueInterface::push($job,$data,$queue) with string handlers + IlluminateQueueClosure vs L13 object jobs routed through CallQueuedHandler@call. Every app-space Queue::push/pushRaw call-site and every job class must be reshaped to L13 idioms (dispatch()/ShouldQueue) before the flip -- this is the real work, not the driver internals.
  • ⚠️ Wire-format change of the queued payload: an L42x-serialized payload on SQS/Redis cannot be consumed by an L13 worker and vice-versa. Requires draining queues at cutover; a mixed-version fleet during rollout will silently fail jobs.
  • ⚠️ Closure serialization moved from Support\SerializableClosure + Encryption\Encrypter to laravel/serializable-closure v2; IlluminateQueueClosure is gone. Any persisted closure jobs are unportable.
  • ⚠️ L13 pulls in Bus (batching/Queueable), Pipeline (job middleware), Foundation and Log as new hard deps -- these must be L13-shaped first or Queue won't boot.
  • ⚠️ Recent local work hardened SqsQueue (SQS SDK v3, empty-response guard) against the L42x SqsQueue; L13 has its own ~20KB SqsQueue -- confirm those local fixes are already covered by L13 so they aren't re-applied to dead code.
  • ⚠️ Iron.io driver (IronQueue/IronConnector/IronJob) is fully removed in L13 -- drop it; the http dep exists ONLY for it (verified: only IronQueue.php + Connectors/IronConnector.php import Illuminate\Http). encryption and http are both dropped in L13 and do not block the flip.

Notes: Core framework component: pure engine, moves only in the final flip (flip-only). Verified SyncQueue/Queue are engine code that fire jobs directly, NOT a shim delegating to a maintained primitive -- so 'done' would be wrong. The L42x string-push API and L13 object-dispatch API are mutually incompatible at both call-site and wire-format level; do not swap internals incrementally. realDeps 'http' is Iron-only (dead in L13; verified only IronQueue+IronConnector import it), 'filesystem' is only Console/FailedTableCommand (verified), 'database' is only Failed/DatabaseFailedJobProvider + failed_jobs.stub (verified) -- none are load-bearing for the runtime queue path (that's container/support/console + the driver's cache/redis). 'encryption' is only the closure-payload crypt and is replaced by contracts in L13. Load-bearing app-facing surface is QueueManager::connection() and driver push/pop, plus Worker/Listener console commands. Actual migration effort lives in app-space: rewrite every job to ShouldQueue objects and every dispatch to L13 dispatch(), then flip this core module wholesale.

Verify note (koreksi adversarial): Re-ran grep on both trees. realDeps CONFIRMED exact (cache/console/container/database/encryption/events/filesystem/http/redis/support -- no hallucinations, no omissions). l13RealDeps CONFIRMED exact (bus/cache/console/container/contracts/database/events/foundation/log/pipeline/redis/support). newL13Deps CONFIRMED as correct set-difference (bus/contracts/foundation/log/pipeline). Public API, CallQueuedHandler@call routing, QueueManager implements Factory+Monitor, laravel/serializable-closure v2 + Contracts\Encryption\Encrypter behind ShouldBeEncrypted, and ~20KB SqsQueue all confirmed by reading files. migrationClass=flip-only and effort=very-high justified (engine replacement, wire-format break, 5 new hard deps). CHANGED: blockers -- dropped 'encryption', 'http', 'filesystem' from the blocker list. encryption and http are removed entirely in L13 (not in l13RealDeps) so an L13-shaped version of them is not required for Queue to flip; filesystem is not in L13 Queue use-statements either (only a composer suggest for the failed-job path) and is non-load-bearing. Per the schema definition (blockers = realDeps not yet done) these three cannot gate a flip that no longer depends on them. Remaining blockers assume container/console/support/database/cache/events/redis are not yet L13-'done' (conservative; no cross-component ledger available here to prove any are already done -- if one is, drop it).


Hashing · done / effort low

Map L13: Hashing Real deps (use): support Blockers: support, contracts Dep baru di L13: contracts

API delta:

Same core API (make/check/needsRehash/setRounds) with IDENTICAL bcrypt hash format via native password_* — stored hashes stay verifiable across the flip. L13 additions: (1) 'hash' now binds a HashManager (multi-driver: bcrypt/argon/argon2id) instead of a bare BcryptHasher; the concrete driver is 'hash.driver'. (2) New methods info(), isHashed(), verifyConfiguration(). (3) Own local HasherInterface replaced by Illuminate\Contracts\Hashing\Hasher contract. (4) Default rounds 10 -> 12. (5) make() throws InvalidArgumentException when a configured length 'limit' is exceeded; catches Error -> RuntimeException. (6) check() (in AbstractHasher, overridden in BcryptHasher) returns false for null/empty hash instead of calling password_verify; BcryptHasher::check also optionally enforces algorithm via $verifyAlgorithm. (7) #[\SensitiveParameter] on plaintext args. (8) options key stays 'rounds' (via cost() helper). (9) provider implements DeferrableProvider instead of $defer property, and registers both 'hash' and 'hash.driver'. (10) setRounds() now returns $this (was void).

Public API (app-facing):

  • BcryptHasher::make($value, array $options)
  • BcryptHasher::check($value, $hashedValue, array $options)
  • BcryptHasher::needsRehash($hashedValue, array $options)
  • BcryptHasher::setRounds($rounds)
  • HasherInterface (make/check/needsRehash)
  • Hash facade -> 'hash' binding

Risks:

  • ⚠️ Default cost changed 10 -> 12: new/rehashed passwords get cost=12; existing cost=10 hashes still verify fine (password_verify reads cost from the hash), so no lockout. Only cost is CPU per login — acceptable.
  • ⚠️ L42x binds 'hash' directly to BcryptHasher; L13 binds it to HashManager (driver resolved from config('hashing.driver','bcrypt')). If app has no hashing config, default is still bcrypt — verify a hashing config array exists or rely on the 'bcrypt' fallback default.
  • ⚠️ App resolves the container 'hash' and uses make/check via the Hash facade (grep of dicoding app: only Hash::make/check/needsRehash + bcrypt(), no direct Illuminate\Hashing\ or HasherInterface imports) — those signatures are unchanged, so callsites need no edits.
  • ⚠️ L42x check() ignores null/empty and always runs password_verify; L13 short-circuits null/'' to false. Any code relying on the old behavior (unlikely) would differ, but returning false for empty hash is strictly safer.

Notes: Both L42x and L13 already sit on native PHP password_hash/password_verify/password_needs_rehash with PASSWORD_BCRYPT — this is the canonical 'done' component (like Encryption->OpenSSL). The top-level L42x composer.json AND the illuminate/hashing package composer.json both require ircmaxell/password-compat (~1.0), a polyfill for PHP <5.5's password_* — obsolete on PHP 8.3 and dropped in L13's composer; remove that require at flip. No engine swap needed. The only real change is the container shape: 'hash' becomes a HashManager backed by Support\Manager, pulling in the Illuminate\Contracts\Hashing\Hasher contract and Contracts\Support\DeferrableProvider. So the blockers are that BOTH 'support' (Manager) and the 'contracts' package must be L13-shaped first; hashing itself is a no-op port. Do NOT port the L42x BcryptHasher/HasherInterface/HashServiceProvider — delete them and use L13's Hashing package as-is at the final flip.

Verify note (koreksi adversarial): Changed blockers from ["support"] to ["support","contracts"]: contracts is a hard L13 dep (HashManager needs Illuminate\Contracts\Hashing\Hasher; provider needs Contracts\Support\DeferrableProvider) that isn't itself 'done', and the record's own notes say contracts must be L13-shaped first — it was missing from blockers. Also tightened apiDelta: check() short-circuit lives in AbstractHasher (BcryptHasher adds optional algorithm enforcement), provider registers BOTH 'hash' and 'hash.driver', and setRounds() now returns $this (was void) — minor omissions. Everything else CONFIRMED by reading all source: L42x realDeps grep = only Illuminate\Support; L13 grep (v13.30.1, framework at /home/agis/www/framework) = Illuminate\Contracts + Illuminate\Support. L42x BcryptHasher uses password_hash(...PASSWORD_BCRYPT...)/password_verify/password_needs_rehash; L13 BcryptHasher+AbstractHasher use the same native funcs -> identical $2y$ format, cross-verifiable. migrationClass 'done' and effort 'low' both justified. Dicoding app consumes hashing only through the Hash facade / bcrypt() with unchanged signatures.


Foundation · flip-only / effort very-high

Map L13: Foundation Real deps (use): console, support, filesystem, http, config, routing, view, container, exception, events, auth Blockers: container, support, config, events, routing, http, filesystem, console, view, exception, auth Dep baru di L13: contracts, queue, database, mail, bus, testing, validation, cache, log, cookie, broadcasting, notifications, session, encryption, translation, concurrency, redis

API delta:

L42x Application is BOTH the DI container AND the HTTP kernel: it extends Container and implements HttpKernelInterface/TerminableInterface/ResponsePreparerInterface (Application.php line 27), bootstraps itself in its constructor (registerBaseBindings/registerBaseServiceProviders/registerBaseMiddlewares, lines 113-118) driven by a procedural start.php, and owns request lifecycle (run/dispatch/handle), global middleware (middleware/forgetMiddleware), lifecycle hooks (before/after/finish), and error handling (error/missing/fatal/pushError/down). In L13 the Application is a pure container/service registry (extends Container implements ApplicationContract, CachesConfiguration, CachesRoutes, HttpKernelInterface; uses Macroable — line 39/41) and every one of those HTTP/error/middleware responsibilities is EXTRACTED: HTTP into Foundation\Http\Kernel, bootstrapping into Foundation\Bootstrap\* (LoadEnvironmentVariables, LoadConfiguration, RegisterFacades, RegisterProviders, BootProviders, HandleExceptions), app assembly into Foundation\Configuration\{ApplicationBuilder,Middleware,Exceptions}, maintenance mode into a MaintenanceModeManager. L42x publishers (Asset/Config/View/Migration) and start.php are gone (confirmed absent in L13 tree); the whole surface is now contract-driven. Env loading moved from Config\EnvironmentVariables to Support\Env (both files confirmed present in their respective trees).

Public API (app-facing):

  • Application::register()
  • Application::boot()
  • Application::make()
  • Application::environment()
  • Application::detectEnvironment()
  • Application::runningInConsole()
  • Application::runningUnitTests()
  • Application::run()
  • Application::handle()
  • Application::dispatch()
  • Application::middleware()
  • Application::forgetMiddleware()
  • Application::before()/after()/finish()
  • Application::error()/missing()/fatal()/pushError()
  • Application::down()
  • Application::isDownForMaintenance()
  • Application::abort()
  • Application::terminate()
  • Application::bindInstallPaths()
  • Application::getLocale()/setLocale()
  • AliasLoader::load()/alias()/register()
  • Artisan::__call()
  • ProviderRepository::load()
  • EnvironmentDetector::detect()
  • Composer::dumpAutoloads()
  • AssetPublisher/ConfigPublisher/ViewPublisher/MigrationPublisher::publish()
  • Testing\TestCase + ApplicationTrait + AssertionsTrait + Client

Risks:

  • ⚠️ Application is the single most load-bearing class in the framework: it is the container root, so it cannot move until Container/Support/Config/Events/Routing/Http are all L13-shaped first.
  • ⚠️ L42x app-space patterns that DISAPPEAR: App::error()/App::missing()/App::fatal() global error closures (bootstrap/global.php), $app->middleware() global middleware registration, App::down() maintenance closure, before()/after()/finish() filters, and the entire start.php / bindInstallPaths bootstrap. Every call-site of these must be reshaped to L13 (Http/Console Kernel $middleware arrays, withExceptions, bootstrap/app.php builder).
  • ⚠️ Foundation\Testing changes completely: L42x Client + ApplicationTrait + AssertionsTrait -> L13 TestCase/concerns; all feature tests reshape.
  • ⚠️ L13 Foundation pulls in a huge dependency fan-out (queue, database, mail, bus, broadcasting, notifications, cache, log, cookie, session, validation, concurrency, redis) via its bundled commands/providers/testing helpers - effectively the whole framework must exist before Foundation is complete.
  • ⚠️ Custom L42x pieces (Artisan wrapper, ChangesCommand/changes.json, custom publishers, StackedHttpKernel/MiddlewareBuilder) have no L13 equivalent and must be dropped, not ported.
  • ⚠️ exception is a realDep+blocker but in L13 it EVAPORATES (absorbed into Foundation, symfony/error-handler underneath) rather than becoming a standalone component; its removal must land at/before the Foundation flip, so it stays a hard prerequisite even though it has no standalone L13 target.

Notes: flip-only: Foundation is the framework core glue and moves entirely in the final flip - there is no incremental in-place swap. It is the LAST major thing to land because it depends on essentially every other Illuminate component being L13-shaped (container, support, config, events, routing, http, filesystem, console, view, exception, auth are the direct L42x imports and the hard blockers). The roadmap (MIGRATION-ROADMAP.md line 191) independently classifies foundation as class=flip, effort=very-high, SCC-2, with exactly these 11 deps. Preparatory work is NOT here but at the CALL-SITES: reshape bootstrap/start.php, bootstrap/global.php error closures, global middleware, and the Testing base class ahead of time (those are reshape-callsites tracked under their own items) so that when the engine flips, app code already speaks L13 idioms (ApplicationBuilder in bootstrap/app.php, Http/Console Kernel, withExceptions, withMiddleware). Treat the L42x custom subtree (Publishers, ChangesCommand, Artisan wrapper, Http\StackedHttpKernel/MiddlewareBuilder, Testing\Client) as delete-on-flip, not port.

Verify note (koreksi adversarial): Confirmed — no field required correction. Re-ran grep -rhoE "use Illuminate\\[A-Za-z]+" on the L42x Foundation tree: realDeps (11) is an exact, complete, non-hallucinated match. l13RealDeps (27) re-grepped against the canonical L13 reference /home/agis/www/framework (Laravel 13.30.1, per roadmap) — exact match, zero missing/extra. newL13Deps (17) verified as the exact set-difference L13\L42x. Class decls read directly: L42x line 27 (extends Container implements HttpKernelInterface/TerminableInterface/ResponsePreparerInterface) and L13 line 39 (extends Container implements ApplicationContract/CachesConfiguration/CachesRoutes/HttpKernelInterface, uses Macroable line 41). All extraction targets confirmed to exist on disk (Foundation/Http/Kernel.php, Bootstrap/{Load*,Register*,Boot*,HandleExceptions}, Configuration/{ApplicationBuilder,Middleware,Exceptions}, MaintenanceModeManager); publishers+start.php confirmed absent in L13; Support/Env.php confirmed present. migrationClass=flip-only and effort=very-high match the roadmap's own foundation row and are justified by the pure-rewrite/extraction (not delegation-to-primitive, so NOT done). blockers set-equals realDeps (11==11, empty diff); cross-checked done-markers in roadmap — only encryption+hashing are done, none of Foundation's 11 deps is done, so blockers=realDeps-not-done holds. Minor label nits only (record cited constructor lines 112-118; actual body is 113-118) — immaterial, left as-is with corrected line refs in apiDelta.


Mail · reshape-callsites / effort high

Map L13: Mail Real deps (use): container, events, foundation, log, queue, support, view Blockers: view, queue, events, container, support, log Dep baru di L13: bus, config, contracts, http, testing

API delta:

L42x is a modernized 4.2 fork ALREADY on symfony/mailer ^6.4 (Symfony TransportInterface/Email/Envelope/Address, Mail\SentMessage wrapping Symfony's SentMessage) — not SwiftMailer. But its API is still the 4.2 contract: single 'mailer' binding + 'symfony.transport' bound via app->bindShared/share, flat config (mail.driver, mail.from, mail.pretend, mail.sendmail), transport picked by a hardcoded switch($config['driver']) in the provider (smtp/sendmail/mail/log; mailgun+mandrill commented out), send() returns void, closure-based message-builder callbacks (or container-resolved 'Class' with ->mail($message)), and queue via serialize(new SerializableClosure($callback)) dispatched as the string job 'mailer@handleQueuedMessage'. L13 (13.30.1, symfony/mailer ^7.4||^8) is completely restructured: (1) a MailManager implementing contracts Factory exposing named/multi-mailer (mailer($name)/build()/createSymfonyTransport()/extend()/purge()), replacing the single binding; (2) Mailer implements Mailer + MailQueue contracts, is per-name, and send() returns a ?SentMessage (not void), plus sendNow(); (3) first-class Mailable (50KB), PendingMail (Mail::to()->cc()->send(new Mailable)), Markdown mail (league/commonmark + css-to-inline-styles), Attachment/TextMessage/Mailables value objects; (4) queueing via the SendQueuedMailable job (serializes the Mailable object), NOT a SerializableClosure string callback; (5) fluent alwaysTo/alwaysReplyTo/alwaysReturnPath, html()/raw()/render(), Events\(MessageSending/MessageSent), and Testing (MailFake). Config moves to the multi-mailer 'mailers.*' array with per-mailer transport definitions.

Public API (app-facing):

  • Mailer::send($view,$data,$callback) (returns void)
  • Mailer::plain()
  • Mailer::queue()/queueOn()/later()/laterOn()
  • Mailer::alwaysFrom()
  • Mailer::pretend()/isPretending()
  • Mailer::handleQueuedMessage(Job,$data)
  • Mailer::getSymfonyTransport()/setSymfonyTransport()
  • Mailer::setLogger()/setQueue()/setContainer()/getViewFactory()
  • Mailer::failures() (deprecated)
  • Message::from/to/cc/bcc/replyTo/sender/returnPath/subject/priority/attach/attachData/embed/embedData/html/text/getSymfonyMessage
  • SentMessage::getSymfonySentMessage()
  • MailServiceProvider (binds 'mailer' + 'symfony.transport', deferred, transport via switch() on mail.driver)

Risks:

  • ⚠️ Engine is already correct (both on symfony/mailer), which MASKS the real gap: the whole API/provider shape differs. Easy to under-scope this as 'done'/'shim-symfony' when it actually needs call-site reshaping.
  • ⚠️ App code that queues mail relies on serialize(SerializableClosure) via the 'mailer@handleQueuedMessage' string job; L13 has no such path — it serializes Mailable objects through SendQueuedMailable. Any in-flight queued-mail payloads and every Mail::queue(Closure) call-site must be rewritten to Mailable classes. Depends on Queue being L13-shaped first.
  • ⚠️ L42x send() returns void; L13 send() returns ?SentMessage — call-sites reading the return (or the removed/deprecated failures()/failedRecipients) must be audited.
  • ⚠️ L42x provider selects transport via a hardcoded switch() with mailgun/mandrill commented out and flat mail.driver config; L13 uses MailManager::createSymfonyTransport with a 'mailers.*' config array. Config files and any custom 'symfony.transport' registration need rewriting; new drivers (ses, resend, mailgun/postmark via symfony) unavailable until then.
  • ⚠️ No Mailable/PendingMail/Markdown in L42x: if app already emits via closure callbacks + Blade views (Mailer resolves views via Illuminate\View\Factory), moving to Mail::to()->send(Mailable) is a broad app-space change, not a lib swap.
  • ⚠️ L13 pulls new use-scan deps (bus, config, contracts, http, testing) absent in L42x — these must be present/L13-shaped before Mail can compile.

Notes: Do NOT classify as 'done' or 'shim-symfony': the Symfony-mailer engine swap was already done in this 4.2 fork (symfony/mailer ^6.4, verified in composer.json + Mailer/Message/SentMessage imports), so there is nothing left to shim at the transport layer. What remains is purely API/structure divergence — single Mailer binding + closure callbacks + flat switch()-based config vs MailManager factory + Mailable objects + SendQueuedMailable + multi-mailer config. That can't be swapped incrementally behind the current API (queue payload format AND send() return type both change), so reshape app call-sites now (introduce Mailable classes, Mail::to()->send(), 'mailers.*' config, drop pretend/failures usage) and flip to L13's MailManager at the end. Blockers are its L42x realDeps that must be L13-shaped first, chiefly view (View\Factory for rendering), queue (job serialization for SendQueuedMailable), and events (MessageSending/MessageSent). Foundation dep is only the provider's Illuminate\Foundation\Application typehint on setMailerDependencies() — evaporates once on L13's ServiceProvider/config, so correctly excluded from blockers.

Verify note (koreksi adversarial): Corrected newL13Deps: removed hallucinated 'collections','conditionable','macroable' — those appear ONLY in L13 Mail composer.json require, NOT in any use-statement (grep for use Illuminate\Collections|Conditionable|Macroable in L13 Mail returns NONE). newL13Deps must be the use-scan diff (L13 realDeps minus L42x realDeps) = {bus, config, contracts, http, testing}. Everything else confirmed empirically: realDeps grep = {container,events,foundation,log,queue,support,view} exactly (record correct). L13 use-scan = {bus,config,container,contracts,http,log,queue,support,testing} exactly matches l13RealDeps (record correct; note this differs from composer require which lists collections/conditionable/macroable/container/contracts/support/http). L42x has NO Illuminate\Contracts import so contracts correctly absent from realDeps. Read L42x Mailer.php (send() returns void, SerializableClosure->'mailer@handleQueuedMessage', pretend/failures) and MailServiceProvider.php (single 'mailer'+'symfony.transport', switch() on mail.driver, Foundation\Application only in setMailerDependencies typehint). L13 (13.30.1): MailManager implements FactoryContract with mailer()/build()/createSymfonyTransport()/extend(); Mailer implements MailerContract+MailQueueContract, send() returns SentMessage, PendingMail/SendQueuedMailable/Mailable(50KB)/Markdown present. migrationClass=reshape-callsites, effort=high, blockers (=L42x realDeps minus foundation) all justified — confirmed. Also expanded publicApi to note send() void return, message html()/text(), and the setter/getter surface (setLogger/setQueue/setContainer/getViewFactory) app code may touch.


Auth · flip-only / effort high

Map L13: Auth Real deps (use): console, cookie, database, events, filesystem, hashing, mail, session, support Blockers: session, cookie, events, hashing, database, support, mail, console, filesystem Dep baru di L13: contracts, queue, http, notifications, cache

API delta:

Monolithic Guard split into SessionGuard + TokenGuard + RequestGuard behind Contracts\Auth\Guard/StatefulGuard (SessionGuard implements StatefulGuard, SupportsBasicAuth). AuthManager rewritten from extends Illuminate\Support\Manager (createDatabaseDriver/createEloquentDriver, getDefaultDriver via auth.driver) to implements Contracts\Auth\Factory with a guard() resolver + createSessionDriver/createTokenDriver/shouldUse/extend. Interfaces UserInterface->Contracts\Auth\Authenticatable (same 5 methods), UserProviderInterface->Contracts\Auth\UserProvider (signatures nearly identical). UserTrait->Authenticatable trait. Reminders subtree (PasswordBroker + DatabaseReminderRepository + RemindableInterface, password_reminders table) renamed/rebuilt as Passwords (PasswordBrokerManager, DatabaseTokenRepository + CacheTokenRepository, TokenRepositoryInterface, CanResetPassword; password_resets table). Brand-new Access\Gate authorization layer (Gate/Response/HandlesAuthorization/AuthorizationException) plus Notifications (ResetPassword/VerifyEmail), Listeners, Middleware, Events subdirs. attempt() drops the 3rd $login arg; adds attemptWhen, logoutOtherDevices, logoutCurrentDevice. Guard ctor no longer takes concrete CookieJar/Dispatcher/SessionStore — wired via contracts; L42x Guard type-hinted Symfony HttpFoundation Request directly, L13 uses Illuminate\Http\Request. App-facing facade surface (Auth::attempt/login/logout/user/check/guest/id/validate/basic) is stable across both versions.

Public API (app-facing):

  • Guard::attempt
  • Guard::login
  • Guard::logout
  • Guard::user
  • Guard::check
  • Guard::guest
  • Guard::id
  • Guard::validate
  • Guard::once
  • Guard::basic
  • Guard::loginUsingId
  • Guard::viaRemember
  • AuthManager::createDatabaseDriver
  • AuthManager::createEloquentDriver
  • UserInterface
  • UserProviderInterface
  • UserTrait
  • GenericUser
  • EloquentUserProvider
  • DatabaseUserProvider
  • Reminders\PasswordBroker
  • Reminders\DatabaseReminderRepository

Risks:

  • ⚠️ remember-token cookie/recaller format differs (L42x Guard::getRecallerName vs L13 Recaller class) — active 'remember me' sessions can be invalidated at flip, forcing re-login
  • ⚠️ Reminders->Passwords rename: any app code or migrations referencing Illuminate\Auth\Reminders* or the password_reminders table/PasswordBroker signature break; L13 uses password_resets/token repositories
  • ⚠️ custom UserProviders/Guards in app-space implement the old UserProviderInterface (5 methods, retrieveByToken signature) — must be reshaped to Contracts\Auth\UserProvider at flip
  • ⚠️ AuthManager no longer extends Manager (now implements Contracts\Auth\Factory); any app code calling createDatabaseDriver/createEloquentDriver or extending the manager the old way breaks — must move to Auth::extend / guard() resolver
  • ⚠️ session driver is now a composer suggest not a hard require; if app relies on stateful session guard, illuminate/session must be present and contract-shaped first
  • ⚠️ attempt() 3rd positional $login arg removed — call-sites passing attempt($creds,$remember,false) must switch to once()

Notes: Pure framework-core auth. call-sites hit the Auth facade (attempt/login/logout/user/check), whose surface is stable 4.2->13, so there is no incremental engine swap worth doing mid-flight — port the whole subtree at the final flip. Depends transitively on session, cookie, events, hashing, database, support (plus mail/console/filesystem from the Reminders+Console subtree) being L13/contract-shaped first, so it must move late in the graph. Two structural renames to plan for: Reminders->Passwords and Guard->SessionGuard/TokenGuard/RequestGuard, and the interface relocation to Illuminate\Contracts\Auth. Access\Gate is a net-new authorization capability (no 4.2 equivalent) that ships for free with the L13 package but is optional to adopt. realDeps taken strictly from use Illuminate\\* statements per instructions (9 entries, matches grep exactly). L42x composer.json additionally declares encryption+http (used via Symfony type-hints / runtime), and L13 composer.json declares collections+macroable (transitive, pulled in but never imported via a use Illuminate\\Collections/use Illuminate\\Macroable statement) — noted for the dep graph but correctly excluded from the use-set-authoritative realDeps.

Verify note (koreksi adversarial): Corrected 3 fields; rest confirmed against code. (1) l13RealDeps was wrong: it listed collections+macroable which are composer require only — NOT use-statements (grep of L13 Auth use-statements returns contracts/support/queue/http/notifications/database/console/cache; zero hits for use Illuminate\\Collections or use Illuminate\\Macroable). Replaced with the authoritative use-set. (2) newL13Deps inherited the same collections/macroable phantoms AND omitted http — http is genuinely new to the use-set (L42x Guard type-hints Symfony\Component\HttpFoundation\Request, no use Illuminate\\Http; L13 imports Illuminate\Http\Request). Recomputed as L13-use-set minus L42x-use-set = contracts/queue/http/notifications/cache. (3) blockers were internally inconsistent (included mail but dropped console+filesystem, all three of which originate only in the Reminders/Console subtree); made blockers the full realDeps set per schema (realDeps not yet done). Verified in code: L42x realDeps grep matches exactly (auth self-ref excluded); L13 AuthManager is implements FactoryContract with guard()/createSessionDriver/createTokenDriver/shouldUse/extend (not extends Manager); SessionGuard implements StatefulGuard/SupportsBasicAuth with attempt/attemptWhen/logoutCurrentDevice/logoutOtherDevices; Passwords/ (PasswordBrokerManager+Database/CacheTokenRepository+CanResetPassword) and Access/Gate.php confirmed present; UserInterface(5 methods)->Contracts\Auth\Authenticatable and UserProviderInterface->Contracts\Auth\UserProvider confirmed. migrationClass=flip-only and effort=high both justified (real framework code ported whole at flip, Guard 3-way split + manager rewrite + Reminders->Passwords rename + interface relocation + remember-me format change).


CachedRouting · evaporate / effort low

Map L13: native Real deps (use): container, events, routing, support Blockers: routing

API delta:

The whole per-file $router->cache($filename, $callback, $minutes) closure-group caching API disappears. L13 replaces it with the global php artisan route:cache / route:clear commands, which serialize ALL registered routes into a single bootstrap-loaded CompiledRouteCollection via Route::prepareForSerialization() — no cache-store TTL, no per-file keys, no manual save/restore of RouteCollection internals. The custom Router/Route/RouteCollection subclasses and the CachedRouting\RoutingServiceProvider binding are all deleted; app code registers routes normally.

Public API (app-facing):

  • Router::cache(string $filename, Closure $callback, int $cacheMinutes = 1440)
  • Router::clearCache(string $filename)
  • Router::routingToController($action)
  • Router::makeControllerActionClosure(array $action)
  • RouteCollection::saveRouteCollection() / restoreRouteCollection() / getCacheableRoutes() / restoreRouteCache($cache)
  • RoutingServiceProvider::register() (rebinds 'router' to CachedRouting\Router)

Risks:

  • ⚠️ App code (in the dicoding app, not this repo) that calls $router->cache(...) to wrap route groups must be de-sugared to plain route registration; grep the consuming app for ->cache( on the route facade/router before flip.
  • ⚠️ The custom controller-action-as-closure indirection (routingToController / makeControllerActionClosure / getControllerAction) is 4.2-era behavior; L13 resolves controller actions natively, so any app relying on that lazy-closure controller resolution needs verification under native routing.
  • ⚠️ L13 route:cache requires ALL routes be cacheable (no Closure-based routes); if the app defines closure routes, caching will fail until they are moved to controllers.
  • ⚠️ Different invalidation model: old shim used cache TTL + filemtime auto-invalidation; native cache is an explicit build artifact (bootstrap/cache/routes-*.php) that must be regenerated on deploy via route:cache in the pipeline.

Notes: Fork of MaartenStaa/laravel-41-route-caching, a userland backport that existed only because L4.1/4.2 shipped NO native route cache. Mechanism (verified by reading files): Router subclasses Illuminate\Routing\Router, installs a CachedRouting\RouteCollection, and Router::cache() reads/writes the LOCAL 'file' cache store (container['cache']->driver('file')) keyed by 'routes.cache.'+cacheVersion('v2')+md5($filename)+filemtime($filename); RouteCollection::getCacheableRoutes() returns serialize(get_object_vars minus 'backup'), with saveRouteCollection/restoreRouteCollection swapping the live collection out and back so only the closure's routes are cached. Route::__sleep() (in Route.php, not re-read here but per record) pre-compiles the Symfony CompiledRoute. Provider rebinds 'router' via $app->share and disableFilters() under env==='testing'. Recent commits (50c74997 persist in local file store, 452e274a bump key to v2) are maintenance on this shim, not signals to keep it. In-repo the ONLY consumers are its own tests (tests/CachedRouting/RoutingServiceProviderTest.php, RoutingIntegrationTest.php) — confirmed by repo-wide grep. L13 does all of this natively and more robustly. Nothing to port: delete the whole dir at flip, drop the provider, and use php artisan route:cache.

Verify note (koreksi adversarial): Confirmed. realDeps grep re-run: exactly Container, Events, Routing, Support -> container/events/routing/support, no hallucinated or missing entries. Public API spot-checked against Router.php, RouteCollection.php, RoutingServiceProvider.php — all methods present as listed (Router::cache reads/writes the LOCAL 'file' store, keyed md5+filemtime, cacheVersion v2; provider rebinds 'router' via $app->share). L13 native path verified: /home/agis/www/framework has Foundation/Console/RouteCacheCommand.php (#[AsCommand('route:cache')], calls prepareForSerialization(), callSilent('route:clear')), RouteClearCommand.php, Routing/CompiledRouteCollection.php, and Route::prepareForSerialization() at Route.php:1544. Only in-repo consumers are tests/CachedRouting/*. migrationClass 'evaporate' is justified (pure userland shim, deleted at flip, no delegation to keep — not 'done'). effort 'low' is sane (delete 4 files + drop one provider binding; API delta is a full removal but mechanical). blockers kept as ['routing'] — the sole realDep gating app-side de-sugaring; the other three realDeps (container/events/support) gate nothing for a component that just evaporates. No field changed.


Html · app-space-extract / effort medium

Map L13: removed Real deps (use): routing, session, support Blockers: routing, session, support

API delta:

Public API is UNCHANGED at flip because there is no L13 counterpart in core to change against. illuminate/html was dropped from Laravel core after 4.2 (confirmed: no Illuminate/Html dir and no HtmlBuilder/FormBuilder anywhere in the L13 framework tree). The L42x classes (FormBuilder, HtmlBuilder, HtmlServiceProvider, MacroableTrait usage) stay byte-for-byte as-is; only their home changes from framework core to an app-space / composer package. No signature migration, no idiom reshape.

Public API (app-facing):

  • FormBuilder::open/close/model/token
  • FormBuilder::text/password/email/url/hidden/textarea/number/file
  • FormBuilder::select/selectRange/selectYear/selectMonth
  • FormBuilder::checkbox/radio/submit/button/reset/image/label
  • FormBuilder::getValueAttribute/getIdAttribute/old
  • FormBuilder::setSessionStore/getSessionStore
  • HtmlBuilder::entities/decode/attributes/obfuscate
  • HtmlBuilder::script/style/image/link/secureLink/linkAsset/linkSecureAsset/linkRoute/linkAction
  • HtmlBuilder::mailto/email/ol/ul
  • Facades HTML:: and Form:: (bindings 'html' and 'form')

Risks:

  • ⚠️ View files across the app use Form:: / HTML:: facades — the API surface must be preserved verbatim or every blade breaks; this is the whole reason to extract rather than rewrite. (The ~497-file figure originates in the app repo /home/agis/www/dicoding and was NOT verified from this framework repo.)
  • ⚠️ FormBuilder depends on session.store->getToken() and old-input (Session\Store, imported as 'use Illuminate\Session\Store as Session') — the extracted package's binding must be wired against whatever Session shim L13 provides; watch getSessionStore()/old() coupling.
  • ⚠️ HtmlServiceProvider uses bindShared() (4.2-ism, confirmed present in L42x Container.php:409 and ABSENT from the L13 container) — must become singleton() when re-registered under L13's container.
  • ⚠️ MacroableTrait is aliased from Illuminate\Support\Traits\MacroableTrait; L13 moved it out of Support into its own top-level component Illuminate\Macroable (dir Illuminate/Macroable/Traits present in L13) and renamed it Macroable — the extracted copy must update the trait import or vendor its own copy of the 4.2 MacroableTrait.
  • ⚠️ FormBuilder imports Carbon\Carbon (third-party, not an Illuminate component, so correctly absent from realDeps) — the extracted package must keep nesbot/carbon as a require.
  • ⚠️ composer.json lists illuminate/http as a require but no use-statement references it directly; the runtime binding leans on 'url' (Routing\UrlGenerator) and 'session.store', so Http coupling is nominal, not code-level.

Notes: Html was removed from Laravel core after 4.2 and lived on as laravelcollective/html (Laravel 5-8) then spatie/laravel-html; neither ships for L13 cleanly, and the app leans on the exact 4.2 Form::/HTML:: signatures across its blade views. Cheapest correct path is app-space-extract: lift these 3 files (FormBuilder, HtmlBuilder, HtmlServiceProvider) out of core into an in-repo package/provider under the app namespace, keep the same 'html'/'form' bindings and facade aliases, and re-point their internal deps (UrlGenerator, Session\Store, Macroable) at L13's equivalents. Do NOT introduce a new abstraction and do NOT rewrite views. The 3 mechanical fixes needed when relocating: bindShared()->singleton(), MacroableTrait->Illuminate\Macroable\Traits\Macroable import (or vendor the 4.2 trait), and re-binding session.store. blockers = routing/session/support must be L13-shaped first so the extracted provider can resolve 'url' and 'session.store' and the Macroable trait. migrationClass is app-space-extract (NOT done): the code delegates to no maintained primitive — it is self-contained HTML/form string generation using only htmlentities/str_replace/rand plus $url->asset/to/route/action.

Verify note (koreksi adversarial): Corrected two false claims in the prior verifyNote. (1) FALSE: prior note said "composer.lock pins illuminate/html: self.version" — grep 'illuminate/html' /home/agis/www/l42x/composer.lock returns 0 matches; the only self.version pin found is illuminate/support in the L13 framework composer.json, unrelated to html. Removed. (2) The "~497 blade files" figure is from the app repo /home/agis/www/dicoding and was NOT verifiable from this framework repo; downgraded to unverified rather than stated as fact. CONFIRMED accurate: realDeps grep = {routing, session, support} exactly (no hallucinated/missing entries; Carbon is third-party, correctly excluded); L13 Illuminate/Html dir ABSENT and no HtmlBuilder/FormBuilder anywhere in framework tree, so l13Mapping='removed' holds; bindShared present in L42x Container.php:409 and absent in L13; L13 Macroable relocated to its own Illuminate/Macroable component (was Support\Traits\MacroableTrait in 4.2); blockers == realDeps and none is 'done'; effort=medium and migrationClass=app-space-extract justified (no delegation to a maintained primitive). Also added omitted public method HtmlBuilder::linkSecureAsset.


Pagination · reshape-callsites / effort high

Map L13: Pagination Real deps (use): http, support, view Blockers: http, support, view Dep baru di L13: contracts, database

API delta:

Entire model replaced. L42x: a DI-injected Factory builds a single Paginator, HTML rendered by PHP Presenter/BootstrapPresenter classes; getter names are get*-prefixed (getCurrentPage, getLastPage, getFrom, getTo, getTotal, getPaginationView); default view is pagination::slider (PHP view). L13 (verified v13.30.1): no Factory object — static resolvers on AbstractPaginator (currentPageResolver, currentPathResolver, queryStringResolver, viewFactoryResolver) wired by PaginationState::resolveUsing($app); three concrete classes LengthAwarePaginator/Paginator/CursorPaginator extending AbstractPaginator/AbstractCursorPaginator; getters renamed (currentPage(), lastPage(), firstItem(), lastItem(), total(), perPage()); HTML rendered from Blade views ($defaultView='pagination::tailwind', $defaultSimpleView='pagination::simple-tailwind') via UrlWindow, Presenter classes deleted; adds Cursor/CursorPaginator, Htmlable/Jsonable/CanBeEscapedWhenCastToString, macroable/Tappable/ForwardsCalls via AbstractPaginator, and a Http\Resources\Json\JsonResource + Eloquent Model/Pivot bridge (TransformsToResourceCollection). useBootstrapThree/Four/Five and useTailwind switch markup.

Public API (app-facing):

  • Factory::make
  • Factory::getPaginationView
  • Factory::getCurrentPage/setCurrentPage
  • Paginator::links
  • Paginator::getUrl
  • Paginator::appends
  • Paginator::fragment
  • Paginator::getCurrentPage
  • Paginator::getLastPage
  • Paginator::getFrom
  • Paginator::getTo
  • Paginator::getTotal
  • Paginator::getPerPage
  • Paginator::getItems/getCollection
  • Presenter (abstract) + BootstrapPresenter
  • PaginationServiceProvider

Risks:

  • ⚠️ Presenter/BootstrapPresenter classes are DELETED in L13 (confirmed: no Presenter*.php in L13 dir) — any app code subclassing Presenter or calling Factory::getPaginationView must move to Blade pagination views (pagination::bootstrap-4/tailwind) before flip.
  • ⚠️ Getter renames are pervasive: getCurrentPage->currentPage, getLastPage->lastPage, getFrom->firstItem, getTo->lastItem, getTotal->total, getPerPage->perPage — every template/controller reading these breaks; Rectorable but must be swept exhaustively.
  • ⚠️ L42x Factory is container-injected and stateful (setCurrentPage/setBaseUrl/setViewName); L13 replaces it with global static resolvers on AbstractPaginator wired via PaginationState — code that manually constructs Paginators (new Paginator($factory, ...)) or manipulates the Factory has no direct equivalent.
  • ⚠️ L13 adds coupling to illuminate/contracts and illuminate/database (JsonResource + Eloquent Model/Pivot bridge) absent in L42x; illuminate/collections is also a new composer require (Support\Collection now lives there), though at the use-statement level it still reads as Illuminate\Support\Collection.
  • ⚠️ L42x default HTML was the PHP pagination::slider view; L13 defaults to Tailwind Blade views — useBootstrapFour()/useBootstrapFive() needed to approximate prior Bootstrap markup, and the slider layout has no L13 equivalent.

Notes: L42x real internal deps (deduped from use-statements) = http, support, view — grep-confirmed, complete, no hallucinations. The 'contracts' seen in L42x is Illuminate\Support\Contracts (ArrayableInterface/JsonableInterface), part of the monolithic support component, NOT standalone illuminate/contracts — correctly folded into 'support'. L42x also depends externally on symfony/http-foundation + symfony/translation via Factory (Request + TranslatorInterface). Correction vs original record: L13 l13RealDeps was listed as [contracts, collections, support, database, http]; by the same use-statement rule applied to L42x, the actual L13 use-statements resolve to {contracts, support, database, http} — 'collections' is NOT imported via a use Illuminate\Collections statement (Collection is imported as Illuminate\Support\Collection), so at the use-statement level it folds into support. It IS a new composer require (illuminate/collections ^13.0), noted in risks. Adjusted newL13Deps from [contracts, collections, database] to [contracts, database] accordingly. Migration is framework core: Factory/Presenter have no L13 shim, so the engine moves at the final flip; because app call-sites use renamed getters and the Presenter HTML mechanism is gone, pre-flip work is call-site reshaping (rename getters, replace Presenter subclasses with Blade pagination views, set useBootstrapFour to preserve markup). Classified reshape-callsites over flip-only for that reason — justified. effort=high is sane given the total model replacement, Presenter deletion, pervasive renames, and new database/contracts coupling. Blockers = realDeps not marked done = {http, support, view} — correct.

Verify note (koreksi adversarial): Corrected l13RealDeps from [contracts, collections, support, database, http] to [contracts, support, database, http] and newL13Deps from [contracts, collections, database] to [contracts, database]: verified L13 (framework v13.30.1) use-statements contain no use Illuminate\\Collections\\... — Collection is imported as Illuminate\Support\Collection, so by the record's own use-statement rule collections folds into support (it remains a composer require, moved to a risk note). All other fields confirmed against actual files: L42x realDeps {http,support,view} grep-exact and complete; L42x Factory DI (Request+ViewFactory+TranslatorInterface), get*-prefixed getters, Support\Contracts interfaces, Presenter/BootstrapPresenter present; L13 static resolvers (currentPage/currentPath/queryString/viewFactoryResolver), renamed getters, PaginationState::resolveUsing, $defaultView=pagination::tailwind, useBootstrapFour/Five, Presenter*.php deleted, JsonResource+Eloquent bridge — all confirmed. migrationClass=reshape-callsites, effort=high, blockers={http,support,view} all justified.

Bagian B — Komponen L13-baru (introduce, 14)


Contracts · introduce / effort low

Map L13: Illuminate\Contracts (new package to introduce) Real deps (use): — Blockers: — Dep baru di L13: database, http, image, support, validation

API delta:

In 4.2 there is NO first-class Illuminate\Contracts package. The only interfaces were the tiny Illuminate\Support\Contracts* bag (ArrayableInterface, JsonableInterface, RenderableInterface, MessageProviderInterface, ResponsePreparerInterface) plus concrete-class type hints and container binding by string key ('app', 'router', etc.). L13 replaces this with a standalone 155-interface package spanning 33 subdomains (Auth, Broadcasting, Bus, Cache, Concurrency, Config, Console, Container, Cookie, Database, Debug, Encryption, Events, Filesystem, Foundation, Hashing, Http, Image, JsonSchema, Log, Mail, Notifications, Pagination, Pipeline, Process, Queue, Redis, Routing, Session, Support, Translation, Validation, View). Two structural shifts: (1) the container binds/resolves by interface name (e.g. Contracts\Container\Container, Contracts\Foundation\Application) instead of the concrete Container/Application; (2) the *Interface suffix convention is dropped (Arrayable, Jsonable, Renderable) and the interfaces move from Support\Contracts into the dedicated Contracts namespace. Queue\ShouldQueue / job-queueing contracts are entirely new.

Public API (app-facing):

  • Illuminate\Contracts\Container\Container
  • Illuminate\Contracts\Foundation\Application
  • Illuminate\Contracts\Support\Arrayable
  • Illuminate\Contracts\Support\Jsonable
  • Illuminate\Contracts\Support\Renderable
  • Illuminate\Contracts\Support\Htmlable
  • Illuminate\Contracts\Support\MessageProvider
  • Illuminate\Contracts\Support\Responsable
  • Illuminate\Contracts\Auth\Authenticatable
  • Illuminate\Contracts\Auth\Guard
  • Illuminate\Contracts\Queue\ShouldQueue
  • Illuminate\Contracts\Events\Dispatcher
  • Illuminate\Contracts\Cache\Repository
  • Illuminate\Contracts\Filesystem\Filesystem
  • Illuminate\Contracts\Routing\ResponseFactory

Risks:

  • ⚠️ The Contracts package composer.json requires NO illuminate/* at runtime (only php ^8.3, psr/container ^1.1.1||^2.0.1 and psr/simple-cache ^1.0||^2.0||^3.0) — the 5 cross-package use-imports (database/http/image/support/validation) are type-hints in a handful of interface bodies (Concurrency\Driver -> Support\Defer\DeferredCallback; Image\Driver -> Image\ImagePipeline; Routing\ResponseFactory -> Http\StreamedEvent; Validation\ValidatorAwareRule -> Validation\Validator; Database\Query\Expression + Database\Eloquent\Casts* + ModelIdentifier -> Database\Eloquent\Model/Grammar/Relation), NOT a composer dependency; do not turn them into a require or you create a cycle since every one of those packages requires illuminate/contracts.
  • ⚠️ App code in the dicoding L42x codebase that references the old Illuminate\Support\Contracts*Interface names (ArrayableInterface, JsonableInterface, RenderableInterface, MessageProviderInterface) must be repointed to the new suffix-less Contracts\Support* names at flip. Confirmed real usage exists, e.g. dicoding/Dicoding/Infrastructure/WebServices/Prakerja/PrakerjaApiResponse.php.
  • ⚠️ Container bindings keyed by string ('app','router','db',...) coexist with interface-keyed resolution; missing an interface binding surfaces only at resolve time, not at boot.

Notes: new-foundation: this component did not exist in L42x, so there is nothing to port or shim — it is added wholesale as part of standing up the L13 framework. It is a pure leaf foundation package: every other Illuminate component depends on Contracts, Contracts depends on none of them at runtime (PSR interfaces only), so realDeps is empty and blockers is empty — it can and should land FIRST, before any component that type-hints or binds against an interface. The 5 L13 cross-imports are docblock/type-hint references only and are the ONE direction where a naive dep-graph would wrongly report a cycle; treat them as non-blocking. Effort is low as framework code (it ships verbatim with L13), but the coupled app-side work — repointing old Support\Contracts\*Interface usages and adding interface-keyed container bindings — is what actually consumes time and is tracked against the consuming components, not here.

Verify note (koreksi adversarial): Verified empirically against real sources; record is substantively accurate. L42x /src/Illuminate/Contracts absent; grep for 'use Illuminate\...' in it returns empty (realDeps [] confirmed). L42x has only Support/Contracts/{Arrayable,Jsonable,MessageProvider,Renderable,ResponsePreparer}Interface.php and no Container contract. L13 reference source is at /home/agis/www/framework/src/Illuminate/Contracts (framework branch 13.x, NOT inside the l42x worktree): 155 .php files; composer.json require = php ^8.3 + psr/container + psr/simple-cache only, zero illuminate/*. Authoritative grep of cross-imports yields exactly database(6)/http(1)/image(1)/support(1)/validation(1) — matches l13RealDeps/newL13Deps. All 15 publicApi contract files exist on disk. CORRECTION: subdomain count is 33, not 34 — the record double-counted a '.github' directory that 'find -type d' picks up but which is not a Contracts subdomain; apiDelta/notes prose adjusted. Structured fields (realDeps, l13RealDeps, migrationClass=introduce, effort=low, blockers=[]) all confirmed correct and unchanged.


Collections · introduce / effort high

Map L13: collections Real deps (use): support Blockers: contracts, macroable, conditionable Dep baru di L13: contracts, macroable, conditionable, http, database

API delta:

L42x: one ~20KB Collection class in Illuminate\Support (src/Illuminate/Support/Collection.php) implementing ArrayAccess, ArrayableInterface, Countable, IteratorAggregate, JsonableInterface, JsonSerializable; 59 public methods (verified), callbacks typed as Closure; only Illuminate imports are Illuminate\Support\Contracts\{Jsonable,Arrayable}Interface. L13: its own composer package illuminate/collections (namespace still Illuminate\Support) — 111 public methods on Collection (verified), now implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable where Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable. Adds sibling classes LazyCollection (52KB, generator-based lazy), HigherOrderCollectionProxy ($c->map->foo), ItemNotFoundException/MultipleItemsFoundException, standalone Arr (36KB), and shared trait EnumeratesValues (58 methods). Callbacks now callable. Many new methods (whenEmpty/unless/pipe/tap/dd/dump, sole/firstOrFail, mapInto, flatMap, sliding, zip, etc.).

Public API (app-facing):

  • Illuminate\Support\Collection
  • make()/collect()
  • all/get/put/pull/forget/has
  • map/mapWithKeys/filter/reject/reduce/each/transform
  • first/last/pop/shift/push/prepend
  • groupBy/keyBy/sortBy/sort/sortByDesc
  • merge/diff/intersect/unique/flatten/collapse
  • contains/search/implode/sum/random/chunk/slice/splice/take
  • toArray/toJson/jsonSerialize
  • ArrayAccess/Countable/IteratorAggregate/JsonSerializable

Risks:

  • ⚠️ L42x Collection is contract-poor: callbacks are strictly Closure (map/filter/each/sort/transform) while L13 accepts any callable — app code passing string/array callables works on L13 but not L42x, so tightening call-sites early is safe but loose ones only surface at flip
  • ⚠️ L42x lacks the huge surface (whenEmpty, pipe, tap, sole, firstOrFail, mapInto, flatMap, higher-order proxy, LazyCollection); app/framework code can't rely on them until flip, and any polyfilling risks divergence
  • ⚠️ Return-type/shape drift: L42x random() default and first()/groupBy/sortBy edge behaviors differ subtly from L13 — silent behavior changes not caught by signatures; needs parity testing
  • ⚠️ New Enumerable interface: app code type-hinting concrete Illuminate\Support\Collection still works, but framework internals now pass Enumerable
  • ⚠️ Soft coupling to http/database via Traits/TransformsToResourceCollection (toResourceCollection) — only illuminate/http is a composer suggest; the Database\Eloquent + Http\Resources use-statements live ONLY in that one optional trait, so autoload must not hard-fail when those packages are absent

Notes: L42x has NO Collections component — the code is a single class at src/Illuminate/Support/Collection.php, so its migration-graph realDep is support (its only two Illuminate imports, ArrayableInterface/JsonableInterface, live under Illuminate\\Support\\Contracts, i.e. the Support component in 4.2). In L13, Collections is split into a first-class foundation package illuminate/collections that keeps the Illuminate\\Support\\ namespace but hard-REQUIRES contracts + macroable + conditionable (via composer require, wired through EnumeratesValues/Macroable/Conditionable traits). It SUGGESTS (not requires) illuminate/http (for toResourceCollection) and symfony/var-dumper (for dump); Database coupling is real only inside the same optional trait. Treat http/database as optional, NOT blockers. Because the public class name and namespace are preserved, most collect()/->map()/->toArray() call-sites migrate transparently; the work is INTRODUCING the modern package + its 3 trait deps, not reshaping callers. Effort is high due to surface-area/behavior-parity testing (59→111 methods + LazyCollection + Arr + Enumerable), not call-site churn.

Verify note (koreksi adversarial): Re-verified against real files; record is materially accurate — realDeps=["support"] is correct (the check's grep targeted a non-existent src/Illuminate/Collections/ dir; the authoritative grep of the actual src/Illuminate/Support/Collection.php yields only use Illuminate\\Support, both Contracts imports under Illuminate\\Support\\Contracts). L42x Collection.php = 19.8KB, 59 public methods (record said ~55; corrected), implements ArrayAccess, ArrayableInterface, Countable, IteratorAggregate, JsonableInterface, JsonSerializable. L13 composer.json require = php^8.3 + illuminate/{conditionable,contracts,macroable} + symfony polyfills (php84/85/86); suggest = illuminate/http + symfony/var-dumper (NOT database — original verifyNote wrongly implied database is a suggest; corrected). L13 Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable, 111 public methods; EnumeratesValues=58 methods. All Http(1)+Database(3) use-statements appear ONLY in Traits/TransformsToResourceCollection.php (optional). migrationClass=introduce justified (no L42x package delegates to a maintained primitive — nothing to mark done/shim). blockers exactly = the L13 hard-requires (contracts/macroable/conditionable), none yet done, http/database correctly excluded. effort=high sane vs the API delta. Corrected: L42x method count ~55→59; verifyNote database/suggest error.


Macroable · introduce / effort trivial

Map L13: Macroable (illuminate/macroable, trait Illuminate\Support\Traits\Macroable) Real deps (use): — Blockers: —

API delta:

L42x ships trait Illuminate\Support\Traits\MacroableTrait inside illuminate/support (no standalone component; file is src/Illuminate/Support/Traits/MacroableTrait.php, 68 lines). L13 promotes it to a standalone illuminate/macroable package as trait Illuminate\Support\Traits\Macroable (SAME namespace Illuminate\Support\Traits, new SHORT name Macroable; file src/Illuminate/Macroable/Traits/Macroable.php, 134 lines). API is a strict superset: L13 ADDS mixin($mixin, $replace=true) (reflects public+protected methods of an object into macros via ReflectionClass) and flushMacros(). macro() signature widens from callable $macro to object|callable $macro (accepts __invoke objects / higher-order macros). Behavior change: L13 binds Closure macros via bindTo() so $this and late-static-binding work inside macros — __callStatic binds to (null, static::class) at line 99, __call binds to ($this, static::class) at line 126 (falling back to null-bind if that throws); L42x uses plain call_user_func_array with NO binding (line 48). Any ported macro that assumes $this simply didn't exist before, so no regression, only new capability. Exception message change: L42x throws Method {method} does not exist. (line 51); L13 throws Method {Class}::{method} does not exist. via sprintf 'Method %s::%s does not exist.' (lines 91-92 / 117-119) — still prefixed with "Method", now includes the class. No app-facing removals.

Public API (app-facing):

  • macro(name, macro)
  • hasMacro(name)
  • __callStatic(method, parameters)
  • __call(method, parameters)

Risks:

  • ⚠️ Short trait name changed MacroableTrait -> Macroable: every use Illuminate\Support\Traits\MacroableTrait and in-class use MacroableTrait; must be renamed (6 sites in core, 2 of which — Html\FormBuilder/HtmlBuilder — evaporate with the Html component; also grep app code for direct usage).
  • ⚠️ L13 binds Closure macros to $this/static — a macro closure in app code that relied on unbound behavior or redefined $this could behave differently (very unlikely but check any app-registered macros with closures referencing $this).
  • ⚠️ macro() no longer type-hints callable; passing a non-callable object now defers the TypeError to invocation time instead of registration time (negligible).

Notes: Both versions are pure-PHP LEAF traits: zero use Illuminate\... deps (only Closure/ReflectionClass/ReflectionMethod/BadMethodCallException/RuntimeException/Throwable from PHP core in L13; nothing imported in L42x). This component is a dependency SINK — L42x MacroableTrait is consumed by EXACTLY 6 core class call-sites (Support\Str, Support\Arr, Cache\Repository, Html\FormBuilder, Html\HtmlBuilder, Support\Facades\Response), each doing use Illuminate\Support\Traits\MacroableTrait; + in-class use MacroableTrait; — so it must exist BEFORE those flip, but it itself blocks on nothing. (The grep also hits src/Illuminate/Foundation/Console/Optimize/config.php, but that only lists the trait file PATH in the optimize cache manifest, not a consumer, and the trait's own definition file — neither is a real dependent.) Migration = (1) pull in illuminate/macroable (or the trait file), (2) rename use ...\MacroableTrait; -> use ...\Macroable; + use MacroableTrait; -> use Macroable; at the 6 sites. The old short name MacroableTrait is gone in L13, so a one-time rename sweep is required; alternatively keep a thin alias trait MacroableTrait { use Macroable; } for a zero-touch bridge during the incremental phase (ponytail: alias only if you actually flip Str/Arr/etc. gradually, otherwise just rename — the 6-site sweep is a 2-minute job, an alias trait is more moving parts than the rename it avoids). Html call-sites (FormBuilder/HtmlBuilder) belong to the L42x-only Html component that evaporates, so 2 of the 6 references disappear anyway, leaving effectively 4 to rename.

Verify note (koreksi adversarial): Confirmed with two corrections. (1) Check-1 grep path in the task (/home/agis/www/l42x/src/Illuminate/Macroable) does NOT exist — L42x has no standalone Macroable dir; the trait lives at Support/Traits/MacroableTrait.php. Re-ran grep against the ACTUAL L42x file and the L13 file with raw grep (rtk's grep wrapper choked on the escaped backslash): both return EMPTY, so realDeps:[] / l13RealDeps:[] / newL13Deps:[] / blockers:[] are all correct, no hallucinated or missing entries. (2) apiDelta exception-message quote was wrong: L13 message is Method {Class}::{method} does not exist. (still starts with "Method"), NOT Class::method does not exist as the original record stated — fixed. Everything else (public API, mixin/flushMacros additions, object|callable widening, bindTo binding at lines 99/126, 6 real consumers vs the config.php/self-def false positives, migrationClass=introduce, effort=trivial) verified by reading both files in full.


Conditionable · introduce / effort trivial

Map L13: new Real deps (use): — Blockers: —

API delta:

Entire component is new in L13; absent in 4.2. L42x has no when()/unless() fluent idiom and no HigherOrderWhenProxy anywhere in Support. (Note: L42x Support\ServiceProvider has an unrelated public when() that returns the array of trigger events for a deferred provider — not the Conditionable idiom; no signature/semantic overlap.) L13 adds the Conditionable trait (when/unless with Closure-resolved values, callback/default arms, and a fluent higher-order proxy when called with 0 or 1 args) plus the HigherOrderWhenProxy that captures a condition then proxies __get/__call to the target.

Public API (app-facing):

  • trait Illuminate\Support\Traits\Conditionable
  • when($value = null, ?callable $callback = null, ?callable $default = null)
  • unless($value = null, ?callable $callback = null, ?callable $default = null)
  • class Illuminate\Support\HigherOrderWhenProxy
  • HigherOrderWhenProxy::condition($condition)
  • HigherOrderWhenProxy::negateConditionOnCapture()

Risks:

  • ⚠️ None functional: zero runtime deps (PHP ^8.3 only). The two files must land together in the monolith at src/Illuminate/Support/Traits/Conditionable.php and src/Illuminate/Support/HigherOrderWhenProxy.php — the trait imports Illuminate\Support\HigherOrderWhenProxy (its only reference), a same-package sibling, so do not split them.
  • ⚠️ Uses func_num_args() to distinguish 0/1/2-arg calls; a hand-rolled callsite or polyfill passing explicit nulls would change branch behavior. Not a concern for a clean introduce.
  • ⚠️ The @template docblocks require nothing at runtime; only matters if static analysis (PHPStan/Psalm) is wired up.
  • ⚠️ Do not confuse the split-package layout with the drop-in target: L13 stores these in the illuminate/conditionable split package dir (framework/src/Illuminate/Conditionable/) but the classes are namespaced Illuminate\Support\Traits and Illuminate\Support — in l42x's monolithic tree they belong under src/Illuminate/Support, NOT a new src/Illuminate/Conditionable dir.

Notes: Brand-new zero-dependency foundation trait. In the L42x dependency graph it is a leaf (no Illuminate imports on the 4.2 side because it does not exist there), so nothing blocks it and it blocks nothing structurally — but many L13 core components mix it in (Collection, Database\Concerns\BuildsQueries, Routing\Route, Http\Request, Pipeline, Bus\PendingBatch/PendingChain/PendingDispatch, Http\Client\PendingRequest, Filesystem, View\ComponentAttributeBag, Validation\Rules\, Testing\, Process\PendingProcess, Notifications\MailMessage, Log). Introduce it EARLY/first alongside Macroable so those components can adopt the shared when()/unless() surface. Drop-in: copy the two L13 files verbatim into the monolith — trait to src/Illuminate/Support/Traits/Conditionable.php (namespace Illuminate\Support\Traits) and proxy to src/Illuminate/Support/HigherOrderWhenProxy.php (namespace Illuminate\Support). Note the trait DOES have one intra-package import (use Illuminate\Support\HigherOrderWhenProxy); realDeps stays [] because that measures the 4.2 side, which has nothing. No app-facing behavior in 4.2 to preserve; purely additive.

Verify note (koreksi adversarial): Corrected file-location/namespace claims in notes/risks/apiDelta; realDeps/effort/migrationClass/blockers confirmed accurate. Re-ran the check grep against /home/agis/www/l42x/src/Illuminate/Conditionable: dir does not exist (grep errors 'No such file or directory'), so realDeps [] is correct and complete — nothing to import. grep -rl Conditionable|HigherOrderWhenProxy over l42x/src returns nothing; L42x Support/Traits contains only CapsuleManagerTrait, ForwardsCalls, MacroableTrait. The only when(/unless( hit in L42x Support is Support\ServiceProvider::when() (unrelated deferred-provider trigger-events method) — not the Conditionable idiom. Original record placed the L13 sources at framework/src/Illuminate/Support/Traits/... and framework/.../Support/HigherOrderWhenProxy.php; those paths do NOT exist — the actual L13 files live in the illuminate/conditionable split package at framework/src/Illuminate/Conditionable/Traits/Conditionable.php and framework/src/Illuminate/Conditionable/HigherOrderWhenProxy.php, namespaced Illuminate\Support\Traits and Illuminate\Support respectively (composer.json requires only php ^8.3, psr-4 Illuminate\Support\). Read both L13 files in full; the trait imports the sibling HigherOrderWhenProxy — the record's 'no Illuminate deps' phrasing was loose and is now clarified. migrationClass=introduce is justified (genuinely additive, no maintained primitive to delegate to); effort=trivial and blockers=[] are sane given zero deps and two tiny self-contained files.


Reflection · flip-only / effort trivial

Map L13: absorbed-into-Support Real deps (use): — Blockers: — Dep baru di L13: support

API delta:

L42x: only Illuminate\Support\Reflector exists — a 947B stub (verified) with a single method getParameterClassName(\ReflectionParameter $parameter): ?string, pure PHP, zero Illuminate deps (imports only \ReflectionNamedType). Consumed by Container internals (Container.php, BoundMethod.php). L13: same class is extracted into a separate illuminate/reflection composer package but KEEPS the Illuminate\Support\ PSR-4 namespace, so the app-facing class name is unchanged (Illuminate\Support\Reflector). L13 Reflector is a backward-compatible superset: adds isCallable(), getClassAttribute()/getClassAttributes(), getParameterClassNames() (union/intersection types), isParameterSubclassOf(), isParameterBackedEnumWithStringBackingType(); getParameterClassName loosens the param type hint but still accepts \ReflectionParameter, so existing call sites are unaffected. L13 also ships a new Illuminate\Support\Traits\ReflectsClosures trait plus global lazy()/proxy() helpers — none of which exist in L42x and none of which L42x code references. (L13 method list is upstream-descriptive; no L13 tree is present in this repo to diff against locally.)

Public API (app-facing):

  • Illuminate\Support\Reflector::getParameterClassName

Risks:

  • ⚠️ No standalone L42x 'Reflection' component exists — the 'new-foundation' framing is nominal. The class already lives in L42x as Illuminate\Support\Reflector; L13 merely repackages it into illuminate/reflection while preserving the Illuminate\Support\ namespace. Do not treat it as a genuinely new top-level namespace to introduce.
  • ⚠️ L13 splits it into a distinct illuminate/reflection package under the Illuminate\Support\ namespace, with illuminate/support requiring illuminate/reflection. This repo vendors Support monolithically (psr-0, single src tree, no reflection sub-package), so at flip keep Reflector + ReflectsClosures physically inside the Support tree rather than reproducing L13's package split — the namespace, not the package boundary, is what app code sees.
  • ⚠️ ReflectsClosures + lazy()/proxy() pull in Support\Collection and modern ReflectionUnionType/IntersectionType, but L42x has ZERO references to ReflectsClosures (grep confirmed) — dead weight at flip unless L13 core code that gets pulled in actually calls them.

Notes: Verified against the actual L42x file. Not a real standalone component: framework-core plumbing that rides along with Support. app-facing surface is exactly Illuminate\Support\Reflector::getParameterClassName, which ALREADY EXISTS in L42x, consumed solely by Container internals (Container.php lines 1031/1098/1130, BoundMethod.php lines 6/214). Nothing depends on the new L13 methods, ReflectsClosures, or lazy()/proxy(). Therefore no call-site reshaping, no shim, no dep to introduce ahead of time — it flips in lockstep with Support/Container at final cutover, replaced by the L13 superset (which is backward-compatible for the one method in use). migration-graph realDeps = [] (the L42x class imports only \ReflectionNamedType, a native PHP symbol; no Illuminate use-statements). l13RealDeps/newL13Deps=[support] reflects the L13 package-level coupling, consistent with realDeps=[] driving the graph. migrationClass flip-only is correct: it is NOT 'done' (the on-disk file is still the old stub, not the L13 superset), and no shim/reshape is needed. effort trivial and blockers=[] both correct (blockers must equal realDeps-not-done = []).

Verify note (koreksi adversarial): Confirmed. All L42x-side claims re-verified empirically: no src/Illuminate/Reflection dir; grep 'use Illuminate\' in Support/Reflector.php returns nothing (only 'use ReflectionNamedType'); realDeps=[] is complete with no hallucinated or missing entries. publicApi getParameterClassName is the sole method, consumed only by Container.php and BoundMethod.php (5 call sites); ReflectsClosures has zero hits in L42x src. blockers=[] matches realDeps=[]. effort trivial and migrationClass flip-only both justified (file is still the old stub → not 'done', backward-compatible superset → no shim/reshape). Only unverifiable item is the L13 method-list detail in apiDelta (no L13 tree in this repo); left as upstream-descriptive and flagged as such. No fields required correction.


Pipeline · introduce / effort low

Map L13: new Real deps (use): — Blockers: contracts, support Dep baru di L13: contracts, support

API delta:

Component is entirely new in L13 — it does not exist in L42x in any form. L42x routes requests through a filter-based dispatch (Router::before/after/callFilter/callRouteBefore, filter names registered via Route::filter). L13 replaces that model with a fluent Pipeline (send->through->then) that carries a $passable through a stack of pipes, each implementing handle($passable, Closure $next). Public surface is net-new: Pipeline (send/through/pipe/via/then/thenReturn/finally/withinTransaction/setContainer) + Hub (defaults/pipeline/pipe/getContainer/setContainer) + two contracts (Contracts\Pipeline\Pipeline, Contracts\Pipeline\Hub) + PipelineServiceProvider (deferred; singleton HubContract=>Hub, bind 'pipeline'=>Pipeline).

Public API (app-facing):

  • Pipeline::send()
  • Pipeline::through()
  • Pipeline::pipe()
  • Pipeline::via()
  • Pipeline::then()
  • Pipeline::thenReturn()
  • Pipeline::finally()
  • Pipeline::withinTransaction()
  • Pipeline::setContainer()
  • Hub::defaults()
  • Hub::pipeline()
  • Hub::pipe()
  • Hub::getContainer()
  • Contracts\Pipeline\Pipeline
  • Contracts\Pipeline\Hub

Risks:

  • ⚠️ Pipeline is the dispatch engine for L13 HTTP Kernel + Router middleware + Bus + Queue; introducing it is inert until those consumers are migrated off L42x's filter/before-after model, so it must be sequenced with Routing/Foundation-Http/Bus/Queue reshaping.
  • ⚠️ Depends on Container resolving pipe class strings (make + method call) and on the Conditionable/Macroable traits existing under Illuminate\Support\Traits — in L13 these ship as separate illuminate/conditionable + illuminate/macroable packages pulled in transitively by illuminate/support; a support-package shape mismatch (missing those sub-packages) breaks Pipeline at autoload time.
  • ⚠️ withinTransaction()/handleCarry() resolve 'db' from the container (make('db')->connection(...)->transaction(...)); only relevant if a pipeline opts into transactions, but the container binding must exist or it throws at runtime.
  • ⚠️ L42x's named-filter concept (Route::filter('auth', ...); Router::callFilter/callRouteBefore) has no Pipeline equivalent; every filter must be rewritten as a middleware class handle($req, $next) at the consumer call-sites.

Notes: Absent in L42x: no src/Illuminate/Pipeline, no src/Illuminate/Contracts/Pipeline, zero "Pipeline" references in the L42x tree. Pure new-foundation drop-in — copy Pipeline.php (8.1K), Hub.php (2.2K), PipelineServiceProvider.php (814B) plus the two contracts (Hub.php 294B, Pipeline.php 740B). Class is small and self-contained; compile-time deps are Contracts\Container\Container, Contracts\Pipeline{Pipeline,Hub}, Contracts\Support\DeferrableProvider (provider only), plus the Conditionable/Macroable traits. NOTE on deps: L13 composer require is contracts + support + conditionable + macroable ^13; in L13 Conditionable/Macroable are their OWN packages (src/Illuminate/{Conditionable,Macroable}/Traits/) but retain the Illuminate\Support\Traits namespace, and illuminate/support itself requires both — so the by-namespace use-statement grep yields only Contracts + Support, and l13RealDeps=[contracts,support] is correct at that level while the literal composer require is wider. It optionally uses DB transactions (withinTransaction/handleCarry resolves 'db' from the container) — suggest-only (suggest: illuminate/database), no hard dep. Effort in isolation is trivial; rated low because introducing it is inert until CONSUMERS reshape — HTTP Kernel, Router middleware stack, Bus dispatcher, Queue CallQueuedHandler all run through Pipeline in L13, whereas L42x uses filters. Heavy lift lives in Routing/Kernel/Bus/Queue, not Pipeline. Pipeline is the shared engine those reshapes depend on, so it lands early (right after contracts + support traits are L13-shaped).

Verify note (koreksi adversarial): Verified empirically against L13 reference tree at /home/agis/www/framework (v13.30.1-10-g). Record is materially accurate; two small corrections applied. (1) publicApi: added Hub::getContainer() (present in Hub.php alongside setContainer, was omitted). (2) Confirmed realDeps=[] (no L42x Pipeline: ls + grep -rln 'Pipeline' over src/Illuminate return nothing; grep of a non-existent dir errors, empty result correct). L13 use-statement grep = Contracts + Support only, so l13RealDeps/newL13Deps/blockers=[contracts,support] stand; added a note that L13 composer require additionally lists conditionable + macroable as their own packages (traits keep Illuminate\Support\Traits namespace, transitively required by illuminate/support) so the by-namespace value is right but understates literal composer deps. migrationClass=introduce justified: files genuinely net-new, do NOT delegate to any L42x primitive (would be wrong to mark done/shim). effort=low sane: component trivial in isolation, cost is in consumer reshaping. blockers == the two L13 realDeps, neither yet done — valid. L42x filter dispatch confirmed at Router.php before(1147)/after(1158)/callFilter(1346)/callRouteBefore(1365). db resolution confirmed at Pipeline.php:136.


Bus · introduce / effort medium

Map L13: new Real deps (use): — Blockers: contracts, support, queue, container, database, foundation, pipeline Dep baru di L13: contracts, support, queue, container, database, foundation, pipeline

API delta:

Entire component is new. L42x has no command-bus abstraction at all (no src/Illuminate/Bus, no src/Illuminate/Contracts/Bus, no Dispatcher contract, no jobs-as-objects, no ShouldQueue-driven dispatch). L42x only ships a Queue component that pushes raw payloads/closures. L13 introduces the whole self-dispatching command-bus surface: Dispatcher (sync + queued + after-response), Queueable/Batchable traits, job chaining, and job batching (PendingBatch/Batch with Database/Dynamo repositories + DebounceLock/UniqueLock). Nothing to diff -- it is additive.

Public API (app-facing):

  • Illuminate\Bus\Dispatcher::dispatch()
  • Dispatcher::dispatchSync()/dispatchNow()
  • Dispatcher::dispatchToQueue()
  • Dispatcher::dispatchAfterResponse()
  • Dispatcher::batch()/chain()/findBatch()
  • Dispatcher::bulk()
  • Dispatcher::pipeThrough()/map()
  • Illuminate\Bus\Queueable trait
  • Illuminate\Bus\Batchable trait
  • Illuminate\Bus\PendingBatch / Batch
  • Illuminate\Contracts\Bus\Dispatcher + QueueingDispatcher contracts
  • Bus facade (Bus::dispatch/batch/chain/fake)

Risks:

  • ⚠️ Batching (DatabaseBatchRepository / DynamoBatchRepository) needs a job_batches table + config('queue.batching.*'); app must run the migration and set driver, or leave batching unused.
  • ⚠️ BusServiceProvider hard-imports Aws\DynamoDb\DynamoDbClient at the top of the file (line 5) and news it at line 82; the class is only make()'d lazily under the dynamo driver, so the database driver path is fine, but aws-sdk must be present if dynamo batching is ever used.
  • ⚠️ Bus is functionally coupled to Queue: real value (queued jobs, chaining, after-response) only appears once Queue is L13-shaped and app jobs implement ShouldQueue. Introducing Bus before Queue is reshaped yields only sync dispatch.
  • ⚠️ Bus reaches back into Foundation (Illuminate\Foundation\Bus\Dispatchable + PendingChain in ChainedBatch/Dispatcher) -- so it is NOT a clean leaf: Foundation must expose those Bus-support traits at the flip. This coupling was missing from the original record's dep list.
  • ⚠️ New idiom for the app: L42x has no jobs-as-objects, so this is a call-site adoption (dispatch(new Job)) not a drop-in -- churn lives in app space, not core.
  • ⚠️ Depends on Pipeline (pipeThrough) and on Collections/Conditionable primitives that here arrive through Illuminate\Support (Support\Collection, Support\Traits\Conditionable), so a correctly-shaped Support carries them; they are not separate first-level imports.

Notes: new-foundation confirmed: /home/agis/www/l42x/src/Illuminate/Bus does not exist, /home/agis/www/l42x/src/Illuminate/Contracts/Bus does not exist, and no app code references Illuminate\Bus. L13 dir: /home/agis/www/framework/src/Illuminate/Bus (18 files: Dispatcher, Batch/PendingBatch/BatchFactory/ChainedBatch, Batchable/Queueable traits, Database/DynamoBatchRepository, Debounce/UniqueLock, BusServiceProvider). composer.json REQUIRES illuminate/{collections,conditionable,contracts,pipeline,support} and only SUGGESTS illuminate/queue -- but the authoritative first-level use-statements (the graph signal) are {Container, Contracts, Database, Foundation, Pipeline, Queue, Support}; collections+conditionable never appear as first-level use-statements (they come via Support). Wired via BusServiceProvider: singleton(Dispatcher::class) with a queueResolver closure resolving Queue Factory contract, aliased to Dispatcher + QueueingDispatcher contracts; registerBatchServices() binds BatchRepository -> Database/Dynamo on config('queue.batching.driver','database'). realDeps=[] because the L42x source has zero use-statements (component absent); l13RealDeps carries the real coupling. Additive package added wholesale at the flip -- nothing to port or shim; app-space job adoption is the real work.

Verify note (koreksi adversarial): Corrected l13RealDeps/newL13Deps/blockers. Re-ran the grep: L42x Bus dir absent (realDeps=[] holds). L13 first-level use-statements are {Container,Contracts,Database,Foundation,Pipeline,Queue,Support}+self. TWO fixes to the original dep arrays: (1) ADDED 'foundation' -- genuinely imported (Illuminate\Foundation\Bus\Dispatchable in ChainedBatch.php:8, PendingChain in Dispatcher.php:10) and was wrongly omitted (the record's own verifyNote even listed Foundation, contradicting its arrays). (2) REMOVED 'collections' and 'conditionable' -- neither appears as a first-level use-statement; Collection is imported as Illuminate\Support\Collection and Conditionable as Illuminate\Support\Traits\Conditionable, i.e. folded into 'support'. They are composer requires, not graph-edge use-statements. Everything else confirmed by reading source: all 12 publicApi methods exist in Dispatcher.php (dispatch:84, dispatchSync:100, dispatchNow:118, bulk:149, findBatch:185, batch:196, chain:207, dispatchToQueue:260, dispatchAfterResponse:308, pipeThrough:326, map:338), contracts aliased (BusServiceProvider:31-38), batching wiring + DynamoDbClient hard-import confirmed. migrationClass=introduce and effort=medium accurate; blockers now equal l13RealDeps (all still to be L13-shaped, none 'done').


Broadcasting · introduce / effort low

Map L13: new Real deps (use): — Blockers: — Dep baru di L13: bus, container, contracts, queue, redis, routing, support, http, foundation

API delta:

No 4.2 counterpart exists — Broadcasting did not ship until Laravel 5.1. In L13 (verified 13.30.1) it is a full package: BroadcastManager (driver factory for pusher/ably/redis/log/null), event traits (InteractsWithSockets, InteractsWithBroadcasting), Channel value objects (Channel/PrivateChannel/PresenceChannel/EncryptedPrivateChannel), PendingBroadcast/FakePendingBroadcast, AnonymousEvent, BroadcastEvent + UniqueBroadcastEvent jobs, BroadcastController. Entirely new public surface.

Public API (app-facing):

  • BroadcastManager::connection()
  • BroadcastManager::driver()
  • BroadcastManager::routes()
  • BroadcastManager::event()
  • BroadcastManager::queue()
  • BroadcastManager::extend()
  • InteractsWithSockets (dontBroadcastToCurrentUser/broadcastToEveryone)
  • InteractsWithBroadcasting::broadcastVia()
  • Channel/PrivateChannel/PresenceChannel/EncryptedPrivateChannel
  • PendingBroadcast::via()/toOthers()
  • AnonymousEvent::dispatch()
  • Contracts\Broadcasting\Factory + Broadcaster

Risks:

  • ⚠️ Not used anywhere in the L42x app or framework (grep for ShouldBroadcast/InteractsWithSockets/BroadcastManager/broadcast( across src/app/bootstrap returns zero real hits; only self-reference is the migration-record artifact), so it is dead weight unless a real-time feature is planned.
  • ⚠️ Real driver use pulls external SDKs (pusher/pusher-php-server ^6|^7 or ably/ably-php ^1) and ext-hash (both 'suggest', not 'require'); RedisBroadcaster additionally couples to a working Illuminate\Redis + PhpRedis/predis stack.
  • ⚠️ Broadcasting jobs ride the queue: correctness depends on the Queue migration (SQS) being solid, since ShouldBroadcast events dispatch BroadcastEvent through the bus/queue.

Notes: Absent from L42x entirely (no src/Illuminate/Broadcasting, no Contracts/Broadcasting, no app usage). Native-in-L13; comes for free with illuminate/broadcasting once the framework is on L13. migrationClass=introduce because it is a new component that would be ADDED — but effort is low and it has NO blockers of its own for the migration graph: nothing in the app depends on it, so it can be wired in at (or after) the final flip via BroadcastServiceProvider (a DeferrableProvider) without touching any call-sites. Note the split between two dep sources: composer.json require = bus, collections, container, contracts, queue, reflection, support (+ psr/log); actual use Illuminate\\X statements = bus, container, contracts, foundation, http, queue, redis, routing, support. l13RealDeps here follows the use-statement set per schema (authoritative = imports via use-statements); collections/reflection are transitive support/macro deps that appear only in composer, not as direct use-statements, and are already dragged in by the core flip. redis/routing/http/foundation are only exercised by specific drivers/routes/controller. Recommend: skip until a feature needs it — YAGNI. ponytail: leave it unregistered; add BroadcastServiceProvider only when the first ShouldBroadcast event appears.

Verify note (koreksi adversarial): Confirmed with two corrections. (1) Re-ran the required grep: /home/agis/www/l42x/src/Illuminate/Broadcasting does not exist and neither does Contracts/Broadcasting, so realDeps=[] is correct. App usage grep across app/src/bootstrap returned zero real hits (only match is the migration-record JSON artifact itself). (2) L13 side verified against the 13.30.1 checkout at /home/agis/www/framework/src/Illuminate/Broadcasting: grep -rhoE "use Illuminate\\[A-Za-z]+" yields exactly bus, container, contracts, foundation, http, queue, redis, routing, support — matching l13RealDeps/newL13Deps with NO hallucinated or missing entry. migrationClass=introduce and effort=low are justified (genuinely new component, zero call-sites, DeferrableProvider wiring); blockers=[] is correct since blockers must be realDeps-not-done and realDeps is empty. Minor corrections vs the original record: added EncryptedPrivateChannel and AnonymousEvent::dispatch() to publicApi (real public classes in the L13 source), and documented the composer-vs-use-statement dep split (composer additionally requires collections+reflection+psr/log, which are not direct use-statements). The original record's prose 'l13Deps' string and 'collections/reflection' mention were internally inconsistent with its own l13RealDeps array; l13RealDeps as given is the correct use-statement set and is unchanged.


Concurrency · introduce / effort low

Map L13: new Real deps (use): — Blockers: process, support, console, contracts Dep baru di L13: console, contracts, process, support, serializable-closure(laravel/serializable-closure ^2.0.10, hard require), carbon(CarbonInterval), spatie/fork(suggest-only ^1.2, fork driver only)

API delta:

Did not exist in 4.2 at all — brand-new package (introduced Laravel 11.x). Entire public surface (Concurrency facade, Driver contract, Process/Fork/Sync drivers, run()/defer()) is net-new. No 4.2 counterpart to diff against.

Public API (app-facing):

  • Concurrency facade / ConcurrencyManager::driver()
  • ProcessDriver::run(Closure|array $tasks, CarbonInterval|int|null $timeout): array
  • SyncDriver::run()
  • ForkDriver::run()
  • *::defer(Closure|array $tasks): DeferredCallback
  • ConcurrencyManager::createProcessDriver/createForkDriver/createSyncDriver
  • InvokeSerializedClosureCommand (hidden artisan cmd 'invoke-serialized-closure')

Risks:

  • ⚠️ Depends entirely on other new-in-13 foundations that L42x lacks: Illuminate\Process (Factory/Pool), Support\MultipleInstanceManager, Support\Defer\DeferredCallback + Illuminate\Support\defer() helper, Contracts\Concurrency\Driver. None of these exist in L42x — Concurrency cannot be introduced until Process + Support/defer land.
  • ⚠️ ProcessDriver re-invokes the app via artisan (Application::formatCommandString('invoke-serialized-closure')) + LARAVEL_INVOKABLE_CLOSURE env, serializing closures with laravel/serializable-closure ^2.0.10 (new hard-require dep). Requires a working L13 console kernel + serializable-closure signing key.
  • ⚠️ ForkDriver needs spatie/fork (suggest-only ^1.2, no Windows) and runningInConsole; not usable in web requests.
  • ⚠️ Zero coupling FROM app 4.2 code — nothing in the current app calls Concurrency, so it is NOT on the flip critical path.

Notes: Absent in L42x (new-optional confirmed: /home/agis/www/l42x/src/Illuminate/Concurrency does not exist, nor do Contracts/Concurrency, Illuminate/Process, Support/MultipleInstanceManager, Support/Defer). Pure additive L13 feature at /home/agis/www/framework/src/Illuminate/Concurrency (ConcurrencyManager extends MultipleInstanceManager; ProcessDriver/ForkDriver/SyncDriver implement Contracts\Concurrency\Driver; hidden InvokeSerializedClosureCommand with #[AsCommand] + $hidden=true). Small, self-contained (6 php files incl. Console cmd, ~56K on disk). It is a leaf consumer, not a dependency of anything — safe to defer to the very end of the migration and only add if the app actually wants concurrent-task execution (YAGNI: skip introducing it unless a call-site needs Concurrency::run/defer). Introduction is trivial once Process + Support/defer exist: register ConcurrencyServiceProvider (singleton ConcurrencyManager, DeferrableProvider), add Concurrency facade, ship config/concurrency.php default. realDeps=[] because there is no L42x version — its real (L13) coupling is console/contracts/process/support, carried in l13RealDeps/blockers.

Verify note (koreksi adversarial): Confirmed. (1) L42x Concurrency dir absent → grep returns nothing → realDeps=[] correct. L13 grep = exactly Console/Contracts/Process/Support, matching l13RealDeps (no hallucinated/missing). (2) Read all 5 drivers + ServiceProvider + Console/InvokeSerializedClosureCommand: every publicApi entry real; run() signature is run(Closure|array $tasks, CarbonInterval|int|null $timeout): array (record had $timeout untyped — corrected). migrationClass=introduce justified: net-new additive package, delegates to NO L42x primitive, so not done/shim/flip. (3) blockers == l13RealDeps == the deps that must land first (none are 'done' since realDeps is empty); effort=low sane (6 files/~56K, zero app coupling). Minor: spatie/fork is suggest-only (^1.2), serializable-closure is a hard require ^2.0.10 — clarified in newL13Deps.


Image · introduce / effort low

Map L13: new Real deps (use): — Blockers: container, support, filesystem, http, contracts, macroable, conditionable Dep baru di L13: container, contracts, filesystem, http, support, macroable, conditionable

API delta:

Did not exist in 4.2 — brand-new component, no prior surface to diff. Introduced (Laravel 12/13 era) as illuminate/image: a Manager-based facade (ImageManager) over intervention/image ^4.0 exposing fluent transforms (resize/cover/contain/crop/rotate/blur/grayscale/sharpen/flip/orient), format conversion (toWebp/toJpg/toPng/toAvif/toHeic/toBmp/toFormat), storage integration (store*/hashName), and an Image object that implements both Responsable (toResponse) and Stringable (toString/toDataUri).

Public API (app-facing):

  • ImageManager::fromPath()
  • ImageManager::fromUpload()
  • ImageManager::fromStorage()
  • ImageManager::fromUrl()
  • ImageManager::fromBytes()/fromStream()/fromBase64()
  • Image::resize()/cover()/contain()/crop()/scale()/rotate()
  • Image::optimize()/quality()/toWebp()/toJpg()/toPng()/toFormat()
  • Image::store()/storeAs()/storePublicly()/storePubliclyAs()
  • Image::toResponse() (implements Responsable)
  • Image implements Stringable (toString()/__toString()/toDataUri())
  • app('image') scoped binding via ImageServiceProvider (DeferrableProvider)

Risks:

  • ⚠️ Real image engine intervention/image ^4.0 is only a composer 'suggest', not a hard require — GD/Imagick drivers throw unless installed with the PHP GD/Imagick extension.
  • ⚠️ ext-fileinfo suggested (mimeType/extension detection).
  • ⚠️ PHP ^8.3 required (composer require.php) — gated behind the same PHP upgrade as the rest of the L13 sync.
  • ⚠️ fromStorage()/store*() couple to L13 Filesystem via Contracts\Filesystem\Factory; fromUrl() uses Http\Client\Factory — both must be L13-shaped to function.
  • ⚠️ macroable/conditionable are standalone illuminate/* packages in L13 but have no separate L42x component (Macroable lived inside Support as MacroableTrait; Conditionable absent) — they are genuine new leaf prerequisites, trivial to introduce.

Notes: new-optional confirmed: /home/agis/www/l42x/src/Illuminate/Image is absent (ls: No such file or directory); zero references to Illuminate\Image, ImageManager, app('image'), or intervention anywhere in l42x app-space or composer.json. Pure opt-in leaf — nothing in the app or framework core depends on it, so it moves at-will and never blocks the flip. ServiceProvider is a DeferrableProvider binding 'image' via $this->app->scoped('image', ...) (verified in ImageServiceProvider.php); no eager wiring. Add only if the app needs server-side image manipulation; otherwise skip (YAGNI). If added: composer require illuminate/image intervention/image + register the provider — no core surgery. Blockers = the full L13RealDep set since L42x-side realDeps is empty; conditionable+macroable added to blockers (record had omitted them despite listing them in l13RealDeps).

Verify note (koreksi adversarial): Corrected blockers: added 'macroable' and 'conditionable' — the original record listed them in l13RealDeps but omitted them from blockers. Confirmed they are real via-use deps: Image.php lines 27-28 import Support\Traits\Conditionable and Support\Traits\Macroable, applied as traits on line 34; and in L42x neither exists as a standalone component (only Support/Traits/MacroableTrait.php bundled inside Support), so both are genuine new prerequisites. The reviewer's suggested grep use Illuminate\\[A-Za-z]+ undercounts — it collapses Support\Traits\Conditionable into 'support'; a deep grep (use Illuminate\[A-Za-z\\]+) yields exactly the 7 deps in l13RealDeps. Everything else verified against actual source at /home/agis/www/framework/src/Illuminate/Image: realDeps [] correct (L42x dir absent), l13 composer.json require matches the 7 deps, all publicApi methods present in ImageManager.php/Image.php, 'implements Responsable, Stringable' confirmed (Image.php:32), scoped('image') binding confirmed. migrationClass 'introduce' and effort 'low' justified (additive new leaf, no callsites to migrate).


JsonSchema · introduce / effort trivial

Map L13: new Real deps (use): — Blockers: — Dep baru di L13: contracts

API delta:

Did not exist in Laravel 4.2 — entirely new component. No 4.2 API to compare against.

Public API (app-facing):

  • JsonSchema::fromArray(array): Types\Type (delegates to Deserializer::deserialize)
  • JsonSchema::object(Closure|array = [])
  • JsonSchema::array()
  • JsonSchema::string()
  • JsonSchema::integer()
  • JsonSchema::number()
  • JsonSchema::boolean()
  • JsonSchema::union(array<int,string>)
  • JsonSchema::anyOf(Closure|array)
  • JsonSchema::__callStatic (proxies to JsonSchemaTypeFactory instance)
  • JsonSchemaTypeFactory extends JsonSchema implements Contracts\JsonSchema\JsonSchema
  • Serializer::serialize(Types\Type): array (static)
  • Deserializer::deserialize(array): Types\Type (static)

Risks:

  • ⚠️ Requires PHP ^8.3 (per its composer.json) — the migration must reach PHP 8.3+ before this package can be installed; it will not load on the L42x-era PHP.
  • ⚠️ new-optional: nothing in a ported L42x app uses it. It lands automatically as a transitive dep of illuminate/foundation (framework/composer.json requires illuminate/json-schema: self.version) once the framework is upgraded, but no app call-site needs it unless the app opts into the schema/structured-output DSL. Introducing it speculatively is YAGNI.

Notes: Brand-new, self-contained leaf component in L13 (illuminate/json-schema). Absent from L42x entirely. Fluent JSON Schema builder DSL: static facade JsonSchema (object/array/string/integer/number/boolean/union/anyOf via __callStatic → JsonSchemaTypeFactory) plus JsonSchema::fromArray()→Deserializer::deserialize() and Serializer::serialize(), with concrete Types/ classes. Its ONLY internal Illuminate dependency is illuminate/contracts (JsonSchemaTypeFactory implements Contracts\JsonSchema\JsonSchema; JsonSchema.php imports only its own Types). No coupling to macroable, collections, conditionable, pipeline, reflection, or the container — zero graph blockers. migrationClass=introduce, OPTIONAL: ships as transitive dep of foundation once the framework upgrades; no dedicated porting work and no app call-site changes unless the app adopts the schema DSL. Effort trivial — nothing to shim, reshape, or flip.

Verify note (koreksi adversarial): Confirmed with two corrections. (1) L42x realDeps=[] verified: /home/agis/www/l42x/src/Illuminate/JsonSchema is absent (ls + grep both error "No such file or directory"), so grep #1 yields nothing and there is nothing to add/remove. (2) l13RealDeps=[contracts] verified: grep -rhoE "use Illuminate\[A-Za-z]+" over the L13 dir returns only Illuminate\Contracts and self (Illuminate\JsonSchema); composer.json require = php ^8.3 + illuminate/contracts ^13.0. Public API spot-checked against source: JsonSchema.php (fromArray→Deserializer::deserialize, __callStatic→new JsonSchemaTypeFactory), JsonSchemaTypeFactory.php (extends JsonSchema implements Contracts\JsonSchema\JsonSchema; object/array/string/integer/number/boolean/union/anyOf), Serializer::serialize and Deserializer::deserialize are static — record's "Serializer" and "Deserializer::deserialize(array)" entries are accurate (added static signatures/return types for precision). framework/composer.json:118 requires illuminate/json-schema: self.version → confirmed first-party split package. migrationClass=introduce justified: brand-new, delegates to nothing maintained-elsewhere, so NOT "done". blockers=[] correct (realDeps empty; even the l13 dep contracts is a foundational leaf that lands first). effort=trivial consistent with 1 dep + no call-sites. Correction to original record's verifyNote wording: (a) it listed JsonSchemaTypeFactory.php twice and Deserializer.php twice; (b) it called the Contracts file the "only referencer outside the component" — imprecise: Contracts/JsonSchema/JsonSchema.php is the contract the component IMPLEMENTS (dependency points component→contract, not contract→component), and grep for consumers of Illuminate\JsonSchema elsewhere in src/Illuminate returns zero. Nothing in the framework yet consumes JsonSchema.


Notifications · introduce / effort medium

Map L13: new (illuminate/notifications, introduced in Laravel 5.3; absent in 4.2) Real deps (use): — Blockers: broadcasting, bus, config, console, container, contracts, database, mail, queue, support Dep baru di L13: broadcasting, bus, config, console, container, contracts, database, mail, queue, support

API delta:

N/A — component did not exist in Laravel 4.2. Entire surface is new in L13. Notifications was added in Laravel 5.3; there is nothing to diff against 4.2.

Public API (app-facing):

  • ChannelManager::send/sendNow/channel/driver/deliverVia/deliversVia/locale/getDefaultDriver
  • NotificationSender::send/sendNow
  • RoutesNotifications trait (notify/notifyNow/routeNotificationFor)
  • Notifiable trait
  • AnonymousNotifiable::route/notify/notifyNow/routeNotificationFor/getKey
  • Notification base class (via() / to<Channel>())
  • MailMessage / SimpleMessage / DatabaseMessage / BroadcastMessage
  • MailChannel / DatabaseChannel / BroadcastChannel
  • DatabaseNotification model + HasDatabaseNotifications
  • SendQueuedNotifications (queued job; uses ReadsQueueAttributes + full Queue\Attributes set)
  • NotificationServiceProvider (binds ChannelManager singleton, aliased to Notifications Dispatcher + Factory contracts)

Risks:

  • ⚠️ Optional: only worth introducing if app actually sends notifications (grep app for ->notify(, Notification::send, extends Illuminate\Notifications\Notification, use Notifiable). If unused, SKIP — do not port speculatively (YAGNI).
  • ⚠️ Heavy dependency fan-out: pulls in mail, queue, bus, broadcasting, database — all must already be L13-shaped/working before this can function, so it is a LATE-stage introduce, not an early one.
  • ⚠️ Database channel needs the notifications migration (uuid morphs) + DatabaseNotification Eloquent model + HasDatabaseNotifications trait wired on notifiable models; requires Database (Eloquent) on L13. NOTE: database is a use-statement dep here, not merely a composer 'suggest' — the DatabaseChannel/DatabaseNotification code imports Illuminate\Database directly.
  • ⚠️ Queued notifications (SendQueuedNotifications) depend on the Bus/Queue serialization stack incl. the new Queue Attributes (Backoff/Tries/Timeout/Connection/Delay/Queue/MaxExceptions/FailOnTimeout/DeleteWhenMissingModels) and ReadsQueueAttributes — relevant to the ongoing SQS migration; verify these queue primitives exist before enabling queued sends.
  • ⚠️ Broadcast channel depends on illuminate/broadcasting which is itself a new-in-5.x component — if broadcasting isn't introduced, restrict channels to mail/database.
  • ⚠️ MailChannel is the richest surface (~9KB: Markdown, Attachment, Attachable, Mailable) — depends on illuminate/mail being fully L13 (Markdown rendering, mailables).

Notes: new-optional, pure application feature not framework core: introduce ONLY on demand. If Dicoding sends transactional email/db notifications today it is likely via the Mailer directly, not this subsystem. Recommended: (a) grep the app for notify()/Notification usage first; if zero hits, mark SKIP for the migration. (b) If needed, it is a standard composer require illuminate/notifications:^13 + register NotificationServiceProvider — no custom porting, since no 4.2 version exists to reconcile. Downstream leaf: schedule AFTER all its realDeps are L13-shaped. No self-written code to migrate; effort is integration/wiring only. CAVEAT on effort: since there is literally zero code to port (component is 100% new, borrowed wholesale from upstream L13), 'medium' is on the high side — the only real work is gating on ~10 upstream components + the DB migration + channel wiring; if those blockers are already satisfied when this is scheduled, actual work here is closer to 'low'.

Verify note (koreksi adversarial): CORRECTED. (1) realDeps=[] confirmed: /home/agis/www/l42x/src/Illuminate/Notifications does NOT exist (ls fails). (2) l13RealDeps corrected 13→10: re-ran grep -rhoE 'use Illuminate\\[A-Za-z]+' on the L13 source (found at /tmp/laravel-framework/src/Illuminate/Notifications AND /home/agis/www/framework/..., both branch 13.x, 13.0.x-dev; identical output). Authoritative use-statement deps = broadcasting, bus, config, console, container, contracts, database, mail, queue, support. REMOVED collections, conditionable, filesystem — these appear ONLY in composer.json require, never as component-level use-statements; Conditionable/Collection are imported via Illuminate\Support\{Traits\Conditionable,Collection}, so they collapse into 'support'. newL13Deps synced to the same 10 (all new since 4.2 had none). (3) blockers corrected: was 8 (dropped config+console arbitrarily); now = all 10 realDeps, since none are marked 'done' and blockers must be exactly the realDeps not-yet-done. (4) Spot-checked public API against real files — ChannelManager/NotificationSender/RoutesNotifications/AnonymousNotifiable methods, SendQueuedNotifications (implements ShouldQueue, uses ReadsQueueAttributes), and NotificationServiceProvider (ChannelManager singleton aliased to Dispatcher+Factory contracts) all confirmed; added getDefaultDriver + getKey/routeNotificationFor that were present but omitted. (5) migrationClass 'introduce' confirmed correct — genuinely absent in 4.2, nothing to reconcile, not 'done' (nothing delegates yet). Kept effort=medium but flagged in notes that it leans high given zero code to port.


Process · introduce / effort low

Map L13: new Real deps (use): — Blockers: support, collections, conditionable, macroable, contracts Dep baru di L13: support, collections, conditionable, macroable, contracts

API delta:

Did not exist in 4.2 at all. New in Laravel 9.2 (2022), present in L13 (verified against v13.30.1 tree). Entire fluent Process API (Factory -> PendingProcess -> ProcessResult / InvokedProcess / Pool / Pipe) plus a full fake/testing surface (FakeProcessResult, FakeInvokedProcess, FakeProcessSequence, FakeProcessDescription, preventStrayProcesses, assertRan*) is 100% additive. It is a thin, opinionated wrapper over symfony/process.

Public API (app-facing):

  • Illuminate\Process\Factory::fake()
  • Illuminate\Process\Factory::pool()
  • Illuminate\Process\Factory::pipe()
  • Illuminate\Process\Factory::concurrently()
  • Illuminate\Process\Factory::assertRan()
  • PendingProcess::command()
  • PendingProcess::path()
  • PendingProcess::timeout()
  • PendingProcess::env()
  • PendingProcess::input()
  • PendingProcess::run()
  • PendingProcess::start()
  • ProcessResult::successful()
  • ProcessResult::failed()
  • ProcessResult::exitCode()
  • ProcessResult::output()
  • ProcessResult::throw()
  • InvokedProcess::running()
  • InvokedProcess::wait()
  • InvokedProcess::signal()
  • InvokedProcess::stop()
  • Process facade (Support\Facades\Process, accessor = Factory::class)

Risks:

  • ⚠️ Requires PHP ^8.3 and symfony/process ^7.4.5||^8.0.5 (per Process/composer.json) — the whole L13 platform floor, not a Process-specific risk.
  • ⚠️ Optional: only worth introducing if app code actually shells out. In L42x that was done ad-hoc via exec()/shell_exec() or a direct new Symfony\Component\Process\Process (e.g. Foundation/Composer.php does exactly this) — those call-sites can be left as-is or opportunistically migrated to Process::run(); not a required part of the flip.
  • ⚠️ Standalone use needs the facade/binding wired yourself: there is NO ProcessServiceProvider and NO 'process' container alias/binding in Foundation at all. The Support\Facades\Process facade simply returns Factory::class as its accessor and relies on the container auto-resolving Illuminate\Process\Factory (no required constructor args). Outside full Foundation you must register the facade alias / ensure Factory is resolvable.

Notes: L42x-absent, correctly classed new-optional. Brand-new self-contained leaf: nothing else in Illuminate depends on it (Concurrency's ProcessDriver make(ProcessFactory::class) is the only internal consumer and is itself optional), so it blocks nothing and is a pure additive convenience. composer.json is authoritative for deps: illuminate/{collections,conditionable,contracts,macroable,support} + symfony/process (external). Internally it imports Support\Collection, Support\Str, Support\Traits\{Conditionable,Macroable}, and Contracts\Process\{InvokedProcess,ProcessResult}. Effort low: no porting, no call-site reshaping, no incremental engine swap — pull the package in once its trivial Support/Contracts blockers are L13-shaped; the facade auto-resolves Factory so no explicit binding is needed under full Foundation, only when used standalone. ponytail: skip introducing at all unless app code has a real need to shell out through a testable API; native exec()/direct Symfony Process keeps working. Add when you want fakeable/assertable process calls or pools/pipes.

Verify note (koreksi adversarial): Verified against real v13.30.1 tree (/home/agis/www/framework). Two corrections vs original record: (1) risk #3 was imprecise — it claimed the Factory is "wired into the container/facade inline in Foundation"; in fact there is NO Foundation wiring, NO ProcessServiceProvider, and NO 'process' alias — the facade accessor is literally Factory::class and the container auto-resolves it (grep of Foundation/Application.php for 'process' alias returns 0). Reworded. (2) Clarified in notes that Concurrency\ProcessDriver is the sole internal consumer (and optional). Empirically confirmed: L42x /src/Illuminate/Process absent (ls: no such file); grep of L42x Illuminate/ shows only Symfony\Component\Process usage (Foundation/Composer.php), never Illuminate\Process. L13 composer.json deps match l13RealDeps exactly. All 21 listed public-API methods confirmed present by grep in Factory/ProcessResult/InvokedProcess/PendingProcess. realDeps=[] correct (component absent in 4.2). migrationClass=introduce justified (genuinely new; 'done' impossible). effort=low sane. Otherwise confirmed.


Testing · introduce / effort medium

Map L13: new Real deps (use): — Blockers: console, contracts, cookie, database, foundation, http, support, view Dep baru di L13: console, contracts, cookie, database, foundation, http, support, view

API delta:

Brand-new package. L42x had NO illuminate/testing package; testing lived in Illuminate\Foundation\Testing as four thin pieces (TestCase, ApplicationTrait, AssertionsTrait, Client) built on Symfony BrowserKit's crawler — assertions like assertResponseOk/see() driven by DomCrawler, no fluent TestResponse, no AssertableJson, no ParallelTesting, no PendingCommand/console assertions, no DB/cache/view assertion constraints. L13 replaces that entire model with a standalone illuminate/testing package: an immutable fluent TestResponse wrapping Illuminate\Http\Response (75 assert* methods in TestResponse alone), fluent AssertableJson, PHPUnit 11/12 integration (Assert extends PHPUnit), paratest-based ParallelTesting, and Constraints/ for DB/HTML/soft-delete assertions.

Public API (app-facing):

  • Illuminate\Testing\TestResponse (75 assert* methods here: assertStatus/assertSee/assertJson/assertRedirect/assertCookie/... — fluent HTTP response assertions)
  • Illuminate\Testing\Fluent\AssertableJson (implements Arrayable; fluent JSON: has/where/whereType/etc.)
  • Illuminate\Testing\AssertableJsonString
  • Illuminate\Testing\ParallelTesting (setUpProcess/setUpTestCase/setUpTestDatabase callbacks, token/option) + ParallelRunner/ParallelConsoleOutput/ParallelTestingServiceProvider
  • Illuminate\Testing\PendingCommand (artisan command assertions: expectsQuestion/assertExitCode)
  • Illuminate\Testing\TestView / TestComponent (Blade view/component assertions; import Illuminate\View\View)
  • Illuminate\Testing\Assert (abstract, extends PHPUnit\Framework\Assert) + TestResponseAssert
  • Concerns\{AssertsStatusCodes, TestDatabases, TestCaches, TestViews, RunsInParallel}
  • Constraints\{ArraySubset, CountInDatabase, HasInDatabase, SoftDeletedInDatabase, NotSoftDeletedInDatabase, SeeInOrder, SeeInHtml}

Risks:

  • ⚠️ Requires PHPUnit ^11.5.50 || ^12.5.8 and PHP ^8.3 — L42x test suites run on old PHPUnit with the BrowserKit crawler; every existing test's assertion style ($this->assertResponseOk(), $crawler->filter(), $this->call()->see()) must be rewritten to the L13 TestCase + fluent $response->assert* idiom. This is a large app-side test rewrite, not a drop-in.
  • ⚠️ TestResponse assertions assume an Illuminate\Http\Response (Symfony HttpFoundation-based) return from the kernel; only usable once Foundation/HTTP kernel + routing are L13-shaped.
  • ⚠️ ParallelTesting depends on brianium/paratest and DB/cache token plumbing — optional (suggest); skip until parallel runs are actually wanted.
  • ⚠️ AssertableJson / JSON assertions depend on Support (Arr, Str, data_get) semantics; harmless but must land after Support is L13.
  • ⚠️ Constraints/*InDatabase couple to Eloquent/Database connection resolver — only meaningful after Database is L13.

Notes: L42x-only dir absent (kind new-optional; confirmed /src/Illuminate/Testing does not exist). Pure ADD: pull in illuminate/testing as-is from L13. NB: its composer.json require is only collections/conditionable/contracts/macroable/support ^13; console/database/http are suggest (view/cookie aren't even suggested) — but the AUTHORITATIVE realDeps here come from use-statements, which resolve to console/contracts/cookie/database/foundation/http/support/view. collections/conditionable/macroable never appear as first-segment use-statements (Macroable is imported as Illuminate\Support\Traits\Macroable, i.e. under support), so they are NOT listed in l13RealDeps/newL13Deps despite being composer requires. It is a leaf test-time dependency — nothing in app runtime imports it, so it can be introduced late and independently, gated only by a working L13 Foundation TestCase + HTTP kernel to produce the TestResponse. Recommended sequencing: land near the END, alongside/after Foundation, once app code runs on L13 so tests migrate file-by-file. Because it is test-only and additive, effort is medium (mostly mechanical test-suite rewrite from BrowserKit-crawler to fluent TestResponse), not a core-flip risk. It is a sink node: nothing else in the graph depends on it.

Verify note (koreksi adversarial): Corrections made. (1) newL13Deps was WRONG: it listed collections/conditionable/macroable (composer-package names) which have ZERO first-segment use-statements, while OMITTING foundation which does appear (RunsInParallel/TestView import Illuminate\Foundation\...). Per the record's own authoritative use-statement standard, and since L42x has no counterpart, newL13Deps must equal l13RealDeps. Rewrote it to the 8 real use-statement deps: console/contracts/cookie/database/foundation/http/support/view. (2) blockers previously omitted foundation but l13RealDeps/newL13Deps included it — aligned all three to the same 8. (3) realDeps [] confirmed empty via failed ls + empty grep of /src/Illuminate/Testing in L42x. (4) l13RealDeps confirmed EXACT via grep -rhoE 'use Illuminate\\[A-Za-z]+' framework/src/Illuminate/Testing | sort -u = console,contracts,cookie,database,foundation,http,support,view (self-ref testing excluded). (5) Public API spot-checked by reading files: Assert extends PHPUnit (abstract), AssertableJson in Fluent/ implements Arrayable, TestView imports View\View + Eloquent\Model, TestResponse has 75 assert* methods (record's package-wide '~200' is plausible but slightly loose; tightened claim to 'in TestResponse alone'). migrationClass=introduce and effort=medium confirmed correct — brand-new additive package with no delegation, so not 'done'; blockers correctly are the L13 deps that must exist before the package can be pulled in.