Commit graph

2,066 commits

Author SHA1 Message Date
Meta Vens
cf60fd0707 Assume a pausing in-game dialog primitive in the tutorial sketch
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:21:12 +02:00
Meta Vens
b450361431 Sketch a tutorial script driven through existing agent primitives
AI-extended material built on Emily Harvey's 2020 tutorial premise (Threefra,
the staged assessment); her text is not reproduced. Beats advance on events and
mark which primitives exist, which are data-only, and which are missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 15:20:56 +02:00
Meta Vens
a3c8d55d83 Record kinematic and rail bodies sharing one detection interface
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:50:20 +02:00
Meta Vens
d1972de0de Record that simulation owns the grabbed loot object and its pose
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:44:13 +02:00
Meta Vens
88bcbc94b0 Test that capability events route only to their own kind's subscribers
CapabilityEventsTests.EachEventKindReachesOnlyItsOwnSubscribers publishes one
of each event kind and asserts every subscriber list holds exactly its own
event -- a subscriber to Absorb must never see a Grab/Melee/Thrust event.
Mutation entry in tests/mutation_tests.py kills a mutant that makes
PublishAbsorb also notify the Thrust stream, plus a no-op control proving the
mutation script's own read/replace/restore path is transparent for this file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:37:07 +02:00
Meta Vens
807c4fab7e Drive the field shield test scene by publishing capability events
FieldTester now owns a CapabilityEvents instance and stands in for the
capability source the simulation doesn't have yet: it publishes AbsorbEvent
on click (cycling DamageType so the operator can see how a type-aware
presenter would behave), GrabEvent(Started) on pickup click, MeleeEvent on
the Melee button, and a ThrustEvent every frame from input -- FieldDriver only
ever reacts through CapabilityPresentationBinder. The damped push/twist state
that used to live directly on FieldDriver.Push/FrontTwist/RearTwist now lives
in FieldTester and is applied through the event instead.

Adds a CapabilityPresentationBinder component to the field object in
FieldShieldTest.unity, alongside the existing added ClickableCollider
component on that same prefab-instance GameObject (same pattern, no
serialized fields to wire). Scene behavior is unchanged: hits, melee, pickups
and flow all look and act as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:36:54 +02:00
Meta Vens
2138aa4560 Make FieldDriver a capability presenter, not an owner
FieldDriver now implements IAbsorbPresenter/IGrabPresenter/IMeleePresenter/
IThrustPresenter by delegating to its existing AddHit/GrabObject/Melee/Push
fields; it still decides nothing about capability rules, only how they render
(fork L, docs/headless-playground-cut.md). Its own click-to-hit wiring moves
out to whatever publishes AbsorbEvent (the test scene, next commit) since a
presenter should not be sourcing its own capability events.

The private nested GrabPhase enum is renamed to TendrilPhase so it doesn't
collide with the new global GrabPhase in CapabilityEvents.cs; this is the only
behavior-preserving rename, tendril timing is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:36:40 +02:00
Meta Vens
450d0731f7 Add presenter interfaces and a capability event binder
IAbsorbPresenter/IGrabPresenter/IMeleePresenter/IThrustPresenter let a Unity
component declare which capability events it visualizes without knowing about
CapabilityEvents itself. CapabilityPresentationBinder is the only thing that
subscribes a presenter to its matching stream, resolving a GrabEvent's plain
target handle to a live Transform (and initial velocity) through delegates its
owner supplies, since the engine-free GrabEvent can't carry a Unity reference.
Neither file knows about FieldDriver or any other concrete presentation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:36:27 +02:00
Meta Vens
fdea06f27f Add an engine-free capability event contract
Fork L (docs/headless-playground-cut.md): pickup, impact absorption, melee
blade and propulsion are separate item capabilities that a future simulation
will own; presentations subscribe and decide nothing. This stands up the wire
shape only -- AbsorbEvent/GrabEvent/MeleeEvent/ThrustEvent plus a per-entity
CapabilityEvents source (UniRx Subject<T> per kind, matching the idiom
already used on Entity) -- with no publisher wired up yet. Compiles into the
headless Aetheria.Shared build with no Unity dependency.

AbsorbEvent carries the hit's DamageType (operator, 2026-09-17) so a future
presenter can vary visuals per type; the damage-type mask itself belongs to
the absorb capability's own data and is out of scope here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:36:14 +02:00
Meta Vens
47131ded68 Record the absorption damage type mask
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:26:30 +02:00
Meta Vens
646495f77a Record that presentation is chosen per item, independently of capability
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:10:24 +02:00
Meta Vens
ebba4a36b5 Test the IFF/grudge/detection rules and the weapon stance gate
IffAndCombatTests builds a minimal headless world (CultCache catalog, a Ship
hull and a gun with InstantWeaponData, an empty Zone/GalaxyZone) and pins:
override decides outright; clearing restores the derived rule; leaving the
zone clears overrides; the derived reciprocity path can't mirror another
entity's override; grudge triggers immediately whether the mark or the
detection happens second, is detection-gated, is sticky, and never fires for
a player-controlled entity; PerceivedStanceOf is unknown until detected;
Weapon.StanceAllowsFire and InstantWeapon.Trigger refuse to fire on a neutral
target and fire on a hostile one (and with no target at all); LockWeapon
builds lock on the shooter's own stance, not the target's; and
ZoneGenerator.EligibleWandererFactions excludes the owner and nearest faction
(and is empty when neither is eligible).

Adds one mutation per rule to tests/mutation_tests.py, all KILLED against
this HEAD (control still GREEN).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:01:31 +02:00
Meta Vens
a5fd8f1d1d Let the player target and mark neutrals, and fix the HUD lock direction
TargetReticle, TargetNext and TargetPrevious now consider all visible
entities except self (hostile and neutral), not just VisibleEnemies, so a
neutral wanderer can be selected. TargetNearest stays enemies-only (it drives
weapon lock) but now picks the nearest target: it was using MaxBy, which
picked the farthest.

Adds a "Toggle Stance" input action (bound to the N key; report names which)
that flips the player's own stance on the current target between hostile and
neutral via Entity.SetIff -- one button for the whole affordance, per the
operator's request. Also keeps the "iff hostile"/"iff neutral"/"iff clear"
console commands next to the existing ones, since they cost nothing extra
alongside the hotkey.

Fixes the HUD lock indicator's hostility check, which read
Target.IsHostileTo(CurrentEntity) instead of CurrentEntity.IsHostileTo(Target)
-- the same backwards direction as LockWeapon, fixed in the prior commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:01:15 +02:00
Meta Vens
89e9871092 Record the tractor beam as generic Tool gear
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:01:04 +02:00
Meta Vens
a351009956 Gate weapon fire on the shooter's own stance, shared by player and AI
Weapon.StanceAllowsFire is the one shared gate: true with no target, else the
shooter's own Entity.IsHostileTo(Target) -- not the target's stance toward the
shooter. It lives on the Weapon base class so every weapon subtype and every
caller (player input, TurretController, AI) goes through the same rule.

Wired at the two places weapons actually start dealing damage:
- InstantWeapon.Trigger (the base for InstantWeapon, AutoWeapon, ChargedWeapon
  and LockWeapon/Launcher, all of which route real firing through Trigger).
- ConstantWeapon.Execute's continuous-beam loop, which stops firing (and
  raises OnStopFiring) the moment the shooter's stance disallows it.

Also fixes LockWeapon's lock-building condition, which read the wrong
direction (target.IsHostileTo(shooter) instead of shooter.IsHostileTo(target))
-- the same backwards check as the HUD lock indicator, fixed alongside this in
ActionGameManager.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:00:59 +02:00
Meta Vens
2b33550cc1 Add manual IFF overrides, event-driven grudges, and detection-gated stance
Entity.SetIff(other, hostile?) is the one owner of a runtime-only, per-entity
override (a ReactiveDictionary, not persisted in packs). IsHostileTo checks it
first, but only for the top-level (non-recursive) query: the derived rule's
internal reciprocity check (recursive=true) skips overrides entirely, so one
entity's override can never leak into another entity's own derived hostility.
An override change refreshes EntityHostility immediately via a dedicated
subscription, so dependents (weapons, HUD, grudges) never need a tick to see
it. Overrides are cleared when an entity leaves the zone (existing removal
subscription).

Non-player entities (ships not IsPlayerShip, stations, turrets) hold a grudge:
WatchForGrudge subscribes to another entity's EntityHostility changes toward
this one, plus this entity's own VisibleEntities.ObserveAdd, so a hostile
stance grudges immediately whether the marking or the detection happens
second -- purely event-driven, no per-tick polling. A grudge only takes while
this entity currently detects the marker (VisibleEntities), and once set it
never re-evaluates (sticky; forgiveness is out of scope until a utility-
evaluation pass exists). Player-controlled entities never grudge automatically.

PerceivedStanceOf(other) exposes another entity's stance toward this one for
UI/AI, gated the same way: unknown (null) until this entity detects them.

Known follow-up: leaving the zone clears overrides, which also clears a
grudge. Left as a known gap per the operator's ruling, not solved here.
Of the per-tick derived-hostility refresh's inputs (Entity.cs ~955-964),
PresencePermitted is already a ReactiveProperty; Faction and zone ownership
are not, so converting that refresh to fully event-driven is future work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:00:41 +02:00
Meta Vens
f732cff9e6 Spawn neutral wanderers so combat has something non-hostile to target
ZoneGenerator now spawns NeutralWandererCount ships (default 2, authored on
ZoneGenerationSettings) from a galaxy faction that is neither the zone owner
nor its nearest faction, using the zone's existing seeded Random so placement
stays deterministic per zone. None spawn when no faction qualifies. The
faction-pool rule is pulled into ZoneGenerator.EligibleWandererFactions so it
is unit-testable without running the full generation pipeline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:00:22 +02:00
Meta Vens
de7b04c518 Record fork L: pickup is a capability, field shields are not universal
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 14:00:01 +02:00
Meta Vens
062d3fd1b2 Record that stance overrides clear on zone exit for now
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:56:25 +02:00
Meta Vens
9f80214492 Note drones as the driver for the parked electronic warfare work
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:27:48 +02:00
Meta Vens
f3917138fd Park the electronic warfare sensor ideas
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:25:47 +02:00
Meta Vens
67a953ee2d Record that stance signals are events gated by detection
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:22:10 +02:00
Meta Vens
0369e46f2f Record the stance rulings: toggle, weapons safe, no mirroring, grudges
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:18:32 +02:00
Meta Vens
46efccbec8 Record the Cut C follow-up and close the provenance schema cut
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:47:33 +02:00
Meta Vens
5e9634efcd Pin Brand's same-maker tie-break with a test and a mutant
Brand()'s ordering by record key was only exercised indirectly, through
a test that gives each candidate product a different maker. Add a test
with two FactionProductData for the same maker and same design, distinct
record keys, asserting Brand returns the one first in record-key order.

Add the corresponding mutant (OrderBy -> OrderByDescending at Brand's
ordering) to mutation_tests.py so a regression there gets caught.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:42:48 +02:00
Meta Vens
fc1c0526fe Census: list every distinct maker, don't invent a single one
MakerOf picked the first product for a design from any maker, the same
tie-break Brand() uses to resolve a runtime item's actual attribution.
That made census silently agree with an attribution it has no business
asserting on its own. Census's job is to report what the catalog says,
including when a design has more than one seller.

MakerOf now lists every distinct maker that sells a design (via its
products, manufacturer set), in record-key order, or "(none)" when no
product sells it. The existing duplicate (maker, design) detector is
unchanged. Verified byte-identical `census` output before and after,
since today's catalog has no design with two makers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:42:37 +02:00
Meta Vens
40055024b5 Record Soul passing provenance Cut C
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:37:11 +02:00
Meta Vens
7941aec861 Record role authoring as required Terminus content
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:35:47 +02:00
Meta Vens
0de7453953 Record the stats and power design direction as a target
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:31:37 +02:00
Meta Vens
847cb9c22c Record provenance Cut C landed
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:16:17 +02:00
Meta Vens
b2036fb8f1 Cut C: rewrite the catalog to clear the Manufacturer slot's drift
Probe Q3's body: re-upsert every stored ItemData with its own key in one
commit, through a throwaway scratch console (not part of the repo). Every
record schema-migrated from key 3 (Manufacturer) as an ignored extra slot
after the field's deletion; the rewrite clears that, matching the probe.

115 ItemData records rewritten, 12,686,267 -> 12,683,061 bytes. Reopening
reports 0 ignored-extra-slot migrations across all 180 stored records.
AetherDb factions/dangling/station-fit/loadout-1/hardpoint-fit are byte
identical before and after; census is also identical, because every design's
old Manufacturer field already agreed with the product that sells it (probe
Q1: 0 designs with more than one maker, 0 unset-maker products).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:09:43 +02:00
Meta Vens
333a73709a Cut C: delete ItemData.Manufacturer, derive census makers from products
The manufacturer does not belong to a design (operator ruling); it lives on
FactionProductData and branding is derived, never stored on the design.

- Delete ItemData.cs key 3 (Manufacturer), with a retired-key comment.
- AetherDb census now attributes a design's maker to the first product (in
  record-key order) that sells it, matching ItemManager.Brand's rule; a
  design no product sells shows "(none)".
- AetheriaStoresTests' catalog fixture moves its Manufacturer reference onto
  a FactionProductData selling the design, and FactionIsASingletonInstance
  reads it from there.
- mutation_tests.py gets a paired mutation for that fixture wiring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:03:15 +02:00
Meta Vens
976e80d782 Record fork E as ruled: keep every weapon and behavior type
Today's catalog is the post-breach minimum, not the consumer set. Fire control
must map every behavior type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:59:53 +02:00
Meta Vens
d78945f66b Record fork T as ruled: targeting system is interior Tool gear
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:56:50 +02:00
Meta Vens
8c74856d44 Record persisted behaviors deleted; Cut C next
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:48:46 +02:00
Meta Vens
8fe09a2303 Delete the persisted-behaviors feature per operator ruling
Operator ruling (docs/item-provenance-cut.md Progress header, 2026-09-17):
delete EntityPack.PersistedBehaviors, IPersistentBehavior,
PersistentBehaviorData and MarkerPersistentBehaviorData outright rather than
keep patching around a feature with no production writer. Key(5) is retired
and left unused; other MessagePack keys are untouched.

Removes the pack/unpack code that read and wrote the field, the feature-only
test ReopenedPackKeepsPersistedBehaviors and its BarePack initializer, and the
matching "not yet reached" mutation notes in tests/mutation_tests.py.
ReopenSurvivesAGeneratedShipWithEntities is kept: it pins the reopen defect
class against a real generated ship, independent of this feature.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:44:37 +02:00
Meta Vens
f9b6263d3d Record Soul passing the Cut B fix batch
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:40:07 +02:00
Meta Vens
59fda60cf2 Record the ruling to delete persisted behaviors
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:28:03 +02:00
Meta Vens
aa96f2ecb4 Record fork W as ruled: per-hardpoint arc override, turrets 360
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:24:54 +02:00
Meta Vens
bf6edcaafe Record the Cut B fix batch and the dead persisted-behavior finding
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:24:30 +02:00
Meta Vens
c2ff543109 Fix F5: mutation_tests.py normalizes line endings and stops counting compile errors as kills
Anchors were authored with bare "\n" and this checkout is CRLF, so every
multi-line anchor matched zero times and aborted before ever mutating
anything. detect_newline() reads each file's own line ending and
translates both anchor and replacement to it before matching, counting
and writing back; the untouched bytes around the match are written
through unchanged, so the restore stays byte-exact.

run_test() now distinguishes a build failure ("Build FAILED"/"error CS"
in the dotnet test output, which never reaches "Passed!"/"Failed!") from
a genuine test failure. A build failure reports ERROR and is never
counted as a kill, since the mutation may have broken something the
target test never got a chance to exercise. Verified with a deliberate
syntax-breaking mutation, run standalone and reverted (not committed).

Added mutants for F1-F4's rules (docs/item-provenance-cut.md, Cut B, and
its F1 ruling). Two of Cut 0's named F1 mutations ("pack drops data",
"unpack ignores the lookup") are not included: no IBehavior implements
IPersistentBehavior, so Pack's filter and Restore's lookup are always
fed an empty set and mutating either body is unobservable through any
live path. Recorded as not yet reached in the script's own comment
rather than paired with a test that can't tell the difference.

Ran on this checkout (CRLF, HEAD 080d3b49) against the CultLib a0813c6
worktree: all 17 mutations behaved as expected (16 KILLED, 1 GREEN
control), run window 2026-09-17T03:18:14Z-03:20:46Z UTC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:22:40 +02:00
Meta Vens
090d5554c6 Record that the simulation is 2D and arcs are planar by rule
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:15:40 +02:00
Meta Vens
a5c108866a Map per-hardpoint firing arcs from item rotation
Imagination pass. Mount direction is Entity.Direction rotated by the item's
rotation; FiringArc 120 is one GameplaySettings default; the check is planar;
HardpointTransforms and the .99 aim checks are deleted. Fork W (turrets) open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:15:09 +02:00
Meta Vens
6963ac7a74 Record the arc default and rotation-derived mount direction
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:12:53 +02:00
Meta Vens
080d3b492c Fix F6: update the stale RunSave.Clear comment to include the ledger
Clear removes every record IsRunRecord names, and RunTypes has included
ProvenanceLedger since Cut B; the comment listed only the four earlier
run record types.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:12:31 +02:00
Meta Vens
561c5b8564 Fix F4: cover GetPrice, the durability/thermal exponent paths, role fills, Produced brand, and multi-zone GC roots
Adds tests for behavior that Cut B shipped without coverage:
- GetPriceReadsLotQuality: price varies with the lot's quality through
  GameplaySettings.QualityPriceModifier.
- EvaluateDurabilityExponentReadsLotQuality: ItemManager.Evaluate's
  durability exponent path reads the lot's quality independently of the
  quality-for-role term.
- EquippedItemThermalExponentReadsLotQuality: EquippedItem's thermal
  exponent (Entity.cs) reads the lot's quality on a real equip.
- CreateLotFillsRolesFromProductSpread: CreateLot(product) fills role
  quality from the product's own per-role spread, not the design default.
- BrandReadsProducedFaction: Brand resolves the maker off a Produced
  origin, not only Attributed.
- CommitTakesRootsFromEveryZone (RunSaveTests): GC roots are collected
  from every committed zone, not only the first.

All six verified by mutation (GetTier/Evaluate/CreateLot/Brand paths
patched to ignore the lot or return a fixed value; RunSave.Commit's roots
truncated to zones.Take(1)) and reverted after confirming the kill.
42 of 42 tests pass on the real code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:11:43 +02:00
Meta Vens
cb2febec90 Fix F3: two tiers and a no-reroll check in StatsReadTheLot
A single-tier fixture cannot fail "GetTier ignores lot quality": every
lot resolves to the only tier regardless of its own Quality. Added a
second tier (Rare, threshold .5) and a second lot whose quality (.6)
lands in it, and asserted GetTier resolves each lot to its own tier.

Also asserts that minting a second instance from an existing lot does
not re-roll the lot's quality.

Verified by mutation, then reverted: GetTier hardcoded to a fixed
quality, and CreateInstance(int) re-rolling the lot's quality, both
fail this test. 36 of 36 tests pass on the real fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:05:09 +02:00
Meta Vens
cf4b9dd99b Record fork A as ruled: firing arcs are per hardpoint
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:04:44 +02:00
Meta Vens
4bda4c8dd9 Record fork F as ruled: resolve on arrival with deviation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:02:58 +02:00
Meta Vens
4fc9070b07 Fix F2: stop verifying the GC walk with itself in MaterializedLotsSurviveSaveAndReload
The test read reloaded instances through EntitySerializer.Items, the same
enumerator RunSave.Commit uses to compute GC roots. Dropping a root from
Items would silently agree with itself: the root's lot would never be
written, and the test would never look for it.

Rewritten to hand-mint one real lot per EntityPack root (Hull, Equipment,
CargoBays, DockingBays, CargoContents, Children, DockingBayContents)
through ItemManager, so the expected lot ids are known independently of
any enumerator, then read the reloaded pack's own fields directly.
Verified: reverting Items to skip the Equipment root now fails this test
with "Lot 2 is not in the provenance ledger" (previously green). Reverted
after confirming the kill; 36 of 36 tests pass on the real fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:02:48 +02:00