Commit graph Aetheria/tools/blender
Author SHA1 Message Date
Meta Vens
bc88f43a19 Bake From Source: one pair under its own names even beside orphans, linked mesh data refused, one error-text site in the operator
Orphan images holding the pair's names are freed at the commit instead of forcing .001; a bake never repoints a slot Source shares through mesh data; the operator's execute is the only place a bake error becomes report text (Refusal marks the module's own fixed text, everything else goes to the console); the copied-material count is asserted. Builds (Blender add-on, no compile step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:47:23 +02:00
Meta Vens
4cea604b85 Bake From Source: the bake never rewrites what Source owns, bakes onto UV0, keeps one pair under its own names, reports Blender errors as fixed text
Shared material (shared-material-source-rewritten): a target slot holding a material a Source mesh uses is given a copy before the bake; a replaced image is removed only when unused.
UV0 (bake-follows-active-uv-not-uv0): every target's active UV layer is held at 0 for the bake and put back.
Relief (normal-tolerance-accepts-near-flat): smoke_bake bounds the normal's median tilt at 3 degrees (copy of Source 0.32, hull 16.59).
Pair names (pair-names-suffixed): a name held by an image the bake would not free refuses with fixed text.
Fixed text (refusal-passes-plain-runtimeerror-text): any foreign exception inside the bake becomes BakeFailed with the console pointer.
All builds; smoke_bake.py passes on Blender 5.2.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:10:34 +02:00
Meta Vens
07852e6313 smoke_bake: pin the link restore, the offender selection, Blender's text staying out of the report, and the preference-to-bake relation
Found by hand mutants that survived: a failed bake restored a Base Color link only when a prior non-image link
existed (the smoke now has one), the offenders were already the selection, the bake's RuntimeError text was not
checked, and the cage/ray windows allowed a constant offset. All commits build; no C# touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:45:59 +02:00
Meta Vens
39713df097 smoke_bake: fixture gaps found by the hand mutants: commit-step rollback, bare-mesh rollback, linked Metallic, bake preferences
The Source gets a linked Metallic and a Transmission Weight of 1, the bake is forced to fail at the
wiring and replace steps and with meshes that have no material, and the cage extrusion and max ray
distance preferences must move the normal map. All commits build; no C# touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:40:20 +02:00
Meta Vens
91bae0dd83 Bake From Source: Cycles Selected-to-Active from Source onto her UV0, one albedo and normal pair per ship
Adds aetheria.bake_from_source: bakes the Source meshes onto the selected render meshes as
'<ship id>.albedo' (sRGB) and '<ship id>.normal' (Non-Color), packed, wired into every material
(a shared '<ship id>.hull' material for a mesh with none). One routine serves both maps and every
target. The Source's Metallic and Transmission Weight are neutralised for the bake (the glTF importer
sets Metallic 1.0, which bakes black); engine, samples, device, selection and Source are restored in
a finally, and a failure removes everything made and puts every changed link back. Three bake
preferences, a panel button, the tripo-hull.glb LFS fixture and smoke_bake.py (4 passes).
_refuse_loose_meshes now shares the select-offenders refusal with the bake.
All commits build; no C# touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:38:26 +02:00
Meta Vens
3535660cc5 Merge cut package-product (Soul verdict cut-package-product.s4) 2026-10-12 20:54:40 +02:00
Meta Vens
6087fd967e zero-byte test asserts cultcache-py's own refusal, so a restored add-on size==0 branch fails it
The test compares type and message with SingleFileMessagePackBackingStore.pull_all on the same
file; the add-on's shape refusal (also a ValueError) no longer passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:00:35 +02:00
Meta Vens
042a13f979 smoke_package: a tall image whose width rounds up pins rounding of the first axis (200x513 -> 100x256, not 99)
Builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:03:13 +02:00
Meta Vens
aa2bf0d41f Guards fix: cultcache-py version is read from the imported module; texture cap boundary, rounding, orientation, group and failure cases pinned
Findings of verdict cut-ships-addon-guards.s1: guard-not-import (the version now comes from the checkout the imported
cultcache_py lives in, one owner, no pyproject-beside-a-path), prefix-compare-survives (0.3.0.1, 0.3.0rc1, 0.3.00
refused), cap-boundary-unpinned and group-restore-unpinned (smoke_package texture_boundary_cases: at the cap, one over,
height-long, non-square rounding, file/packed/node-group images, failed export restores). A copy whose scale raises is
removed. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:59:24 +02:00
Meta Vens
ae3740a050 Fix s2 findings: product validate/inspect/key-collision/price-1 tests; ship_cc.read drops the zero-byte compensator
ship_cc.read lets cultcache-py refuse a zero-byte file (CultLib pins that); the test expects the refusal. New behavioural tests: ValidateJudgesProductsOnADraftAndInspectListsThem (through ShipAuthoringCommands.Run), AProductKeyAlreadyInTheCatalogExcludesThePackage, and price 1 in ASoldPackageHullNeedsAPrice. Tests only plus the add-on deletion; all commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:38:09 +02:00
Meta Vens
873ee6eae4 Guards tests: version drift of any size is refused; the texture fixture's aspect is not a power-of-two ratio
The untested-cultcache test now refuses 0.4.0, 0.3.1 and 0.3 as well as 9.9.9 (a major-only comparison survived).
smoke_package's hull map is 4096x1000, so the scaled size (2048x500, 512x125) is not what a clamp of both sides or of
the other axis would give. Builds: yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:41:08 +02:00
Meta Vens
4acf05da46 smoke_mounts: the mirror matrix on wide grids, 14 and 15 cells across
Passes 7 and 8 run mirror_matrix on ships whose grids are 14 (even) and 15 (odd) wide; the expected twin cells are
grid_w - a - w computed in the matrix itself. Pass 6 (5 wide) now runs through the same matrix_pass function. This is
the commit that adds the cases for follow-up mirror-wide-grid-test. Builds: yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:38:42 +02:00
Meta Vens
5545c850a2 Add-on: Max texture preference; ship.glb exports larger material images scaled
_export_glb points each Image Texture node of the exported objects' materials whose image is longer than the Max texture
preference (default 2048) at a scaled copy (longest side at the cap, aspect kept) and restores the originals and removes
the copies in its finally block, so the .blend's images are never modified. smoke_package.py reads the GLB's PNG
headers: a 4096x1024 map exports at 2048x512 and at 512x128 under a 512 cap, a 256x256 map exports as it is, the .blend
images keep their size and no copy is left in bpy.data.images. Builds: yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:37:50 +02:00
Meta Vens
4f9ada5a6f ship_cc: refuse a cultcache-py that is not the tested version
_libraries reads [project].version from the pyproject.toml beside the cultcache-py source before importing it and raises
ValueError naming the version found and the packages path when it differs from TESTED_CULTCACHE_PY (0.3.0) or the file is
missing. Builds: yes. test_refuses_a_file_with_no_record fails as before (cultcache-py unpack error on a 0-byte file).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:31:46 +02:00
Meta Vens
a486a7cae2 ProductAndPriceCommands reads the reopened cache's own product handle; ship_cc.read treats a zero-byte file as holding no records
The test asked a second cache for the handle of an object loaded by the first (identity lookup, no handle); it now asks the reopened cache for its product. cultcache-py raises an Unpack error on a zero-byte file, so ship_cc.read names the empty file through its own shape refusal. Both commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:38:53 +02:00
Meta Vens
96b255541c Mirror Mount reflects the footprint's cells about the centre line, derived once from the footprint
hull_grid.mirror_position gives the twin Position as grid_w - a - w, so an even-width footprint mirrors to its true
reflection and the cell lookup's tie rule no longer decides a mirror; the twin's object stands in the cell that Position
puts it in, so Package derives the same Position back. test_hull_grid pins the reflection on odd and even grids and
rows of 4 or more cells; smoke_mounts runs footprint widths 1 to 4 at every cell of an even and an odd grid, the
boundary tie, the armour and arc copy, the muzzle's local X, the centre-line threshold, the grid edge and the parent
check, and keeps a mirrored pair of two-cell footprints through Package and the validator. Every commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:33:26 +02:00
Meta Vens
56129b43d5 Package derives no mounts for a ship with no Ship Root, and refuses a framed one whose layout is not loaded
smoke_package's hand-tagged ship has a weapon-mount object, no Ship Root and no loaded
buffer; the first mounts commit refused it. A ship with no frame has no grid to derive
from, so Package leaves its rows alone as before. A framed ship with a mount and an
unloaded buffer is refused with 'Load Layout first'. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:03:12 +02:00
Meta Vens
cc77c4b5f5 ship_cc carries product envelopes through every push; PRODUCT_SCHEMA pinned; authoring doc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:54:31 +02:00
Meta Vens
a89d885c6b smoke_mounts: an asymmetric footprint shows Mirror Mount turning it
All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:47:44 +02:00
Meta Vens
42227f3435 Blender add-on weapon mounts: Add Weapon, Add Muzzle, Mirror Mount, Remove Mount; Package derives mounted Position and yaw
hull_grid gains mounted_position, facing_yaw, mirror_rotation and mirror_footprint. The
mount helpers edit the layout buffer and create a tagged weapon-mount object with its row
as a pair; Package derives each mounted row's Position from its object and each mount's yaw
from its row's Rotation, then saves the buffer through _save_layout. The panel gets a
Mounts box and greys a mounted row's x and y. smoke_mounts.py pins the gunboat flank,
derive and remove paths. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:45:12 +02:00
Meta Vens
429c651a2a Package: sweep only what it generated (aetheria.generated), pin bounds centre, Source-only and collider-in-Generated
Stale sweep identified generated anchors by collection membership and role, so an author's hull-collider in
Generated was deleted. Generated anchors now carry aetheria.generated; the sweep removes only those. New smoke
pass package_asymmetric_pass: an L-shaped render hull pins tractor X and shield centre to the bounds centre,
a Source-only ship pins that generation never reads the Source fallback, and a collider placed in Generated
survives Package. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:55:18 +02:00
Meta Vens
5a92b74e52 smoke_frame: pin the shield floor, the no-outline refusal before any change, and the collider check ahead of the verdict
Hand mutants (floor dropped; recentre before generation) survived the first Package pass; both now fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:24:02 +02:00
Meta Vens
8d9acf269d Package recentres on the grid's centre of mass and generates map icon, shield, tractor; no collider
AetherDb ship-authoring centre prints Shape.CenterOfMass; Package asks it and moves Ship Root so that point sits
at the world origin, after regenerating the unowned map icon, shield and tractor from the render meshes (Keep
leaves one alone). Package never makes a hull collider (ruling package-frame-defer-collider). smoke_package's
bound-collection rollback cases now rebind to a different path so a rollback that restores keys but not values fails.

C# not yet built when committed; the Blender smokes and Python unit tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:02:22 +02:00
Meta Vens
92b82e9aa5 Ships add-on: all-or-nothing bind, pin the cross-drive fallback
_bind_collection restores every aetheria.* key if the ID-property store
refuses a value, so a bad ship id or pending leaves the collection as it
was. smoke_package.py gains default-run cases: relpath raising ValueError
(the cross-drive signal) must store the absolute path and finish; a refused
id or pending must leave bound and unbound collections unchanged.
All commits build (Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:38:17 +02:00
Meta Vens
54ac0fcfa7 Merge eureka/aetheria-release-ships-addon-frame-r4: ships authoring tool and add-on frame
# Conflicts:
#	tests/Aetheria.Shared.Tests/ShipSchemaPinTests.cs
2026-10-10 19:54:03 +02:00
Meta Vens
e058843a34 Add-on frame fix 2: New Ship drafts the hull it leaves; Save, New Ship and undo-past-Save are pinned
New Ship places each mesh by the matrix an object can hold (_held: location, rotation, scale), so a sheared parent no
longer makes the drafted cells differ from the cells Rasterise reads from the hull left behind. smoke_frame pins a failed
Save of a saved ship (write failure and stale revision) leaving its ship.cc, New Ship refusing a vertical wall before
anything changes (twice), the sheared-parent equality, and Ctrl+Z past a first Save: the refusals name the way forward
(Bind Ship Collection, Load Layout) and the route works. All commits build.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 12:56:58 +02:00
Meta Vens
02d9a5219e New Ship: the placement is the objects' matrix, so the cells are the hull the author sees
New Ship baked rotation and scale into the mesh data after checking the cells on the evaluated mesh, so a Mirror
modifier acted in a changed frame (a half hull, length 10, got 3x5 cells). The meshes now keep their data and carry the
placement as their own matrix; the cells and grid_origin are the ones computed before the bind and are placed by the one
_place_grid writer, which Rasterise shares; no call that can refuse follows the bind. The single-user refusal, which
protected the baking, is deleted. One _triangles_xy reads the evaluated meshes for both New Ship and Rasterise.
smoke_frame: a Mirror half hull gets the cells of the hand-mirrored solid, and the drafted cells equal Rasterise's of the
hull left behind. Every commit builds (Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:26:42 +02:00
Meta Vens
9b78940766 smoke_frame: pin the eight refusal guards the smoke left unpinned
Package refuses loose meshes and a pending ship; Package reads the .cc rows cut to the seven known members and only
compares a buffer that belongs to the bound ship; Flip Nose turns the hull back when its Rasterise fails; New Ship
refuses meshes in child collections; the Grid is not drawn from a buffer short of cells; Rasterise refuses a ship with
no Ship Root. Eight hand mutants, all caught. Every commit builds (Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:23:51 +02:00
Meta Vens
47cc0c28db Add-on: an unparented Source mesh is loose; Package reports no library text
One frame predicate: _loose_meshes no longer exempts Source, so a mesh Rasterise reads is a mesh Flip Nose turns (it
turns Ship Root's descendants) or the operator refuses and selects it. Package: the glTF exporter's RuntimeError is
printed to the console and reported as fixed text; the validator's verdict goes to the console and the panel reports
accepted or refused with the anchor count only (no ship ID, no anchor ID). smoke_frame and smoke_package pin each, five
hand mutants caught. Every commit builds (Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:20:41 +02:00
Meta Vens
0906eb53e7 smoke_frame: pin the guards that protect an author's files and the UNDO flags
Pass 7: a pending Save over an existing ship.cc is refused and leaves it; a failed Save keeps the files that were in
the ship folder; New Ship takes no hardpoints from the ship before it; a buffer that belongs to another ship is neither
saved nor rasterised. Pass 8: operators called with undo=True push a native step in background Blender only when they
carry UNDO, so Rasterise, Flip Nose and New Ship undo as one step and Save does not. Nine hand mutants, all caught.
Closes no finding; every commit builds (Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:16:41 +02:00
Meta Vens
aab6654cc1 smoke_frame: a Grid placed for another size is removed after a resize
Builds: yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:52:51 +02:00
Meta Vens
caa418d6cd smoke_frame: a refused create and a nested render mesh; test_ship_cc: refusals name no input
Builds: yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:50:52 +02:00
Meta Vens
5beb693c00 Add-on: the .cc written only by Save; New Ship, Rasterise and Flip Nose edit the undoable buffer
One chooser (_ship_collection) and one predicate (_is_ship). _save_layout is the only
replace_layout caller and creates a pending ship's file, removing what it made on failure.
Rasterise writes the layout buffer and aetheria.grid_origin = [x, y, width, height]; Flip Nose
and Package refuse meshes outside Ship Root. New Ship checks first and writes nothing to disk.
Operator reports are fixed text; AetherDb output goes to the system console. smoke_frame covers
undo, hardpoints, Save, cleanup, loose meshes and an empty Game folder preference.

Builds: yes (needs the previous commit's ship_cc).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:49:40 +02:00
Meta Vens
0b5cad8903 ship_cc: layout_revision and valid_ship_id as pure functions, fixed-text errors; pins and tests for the authoring-tool Low findings
layout_revision(shape, hardpoints) is the revision rule replace_layout and Package share.
SHIP_ID_CHARS and RESERVED_SHIP_IDS mirror ShipAuthoringStore.RequireShipId; ShipSchemaPinTests
sweeps every character and every candidate stem against the C# rule. ship_cc errors no longer
carry the path, mount id, anchor id or object name. C# tests: create judges the ID not the
display name, FindRoot answers the working directory itself, short trailing-dot IDs.

Builds: yes (C# test edits and Python only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:49:39 +02:00
Meta Vens
7a195d2384 Pin the rig's slot 5 carry-through: C# slot numbers and Python replace_visual/replace_lines round trips
Tests only; builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 08:29:12 +02:00
Meta Vens
fb030ca9c3 smoke_package: a folder holding only a ModTools tool is not a game folder
Pass 4 gains the case that kills the mutant dropping the Aetheria.cc check on the Game folder preference. Builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:27:04 +02:00
Meta Vens
c98ad8f56a Add-on: _aetherdb joins its output lines with a newline escape, not a raw newline
The previous commit's _aetherdb did not parse (unterminated string literal), so 63d16e81 and 6971ff70 cannot import the add-on; this one can.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:25:05 +02:00
Meta Vens
63d16e81f3 AetherDb for ship authors: rooted at a game folder, found in ModTools; ship-ID rule in create
FindRoot keys on GameData/Aetheria.cc. ShipAuthoringStore.RequireShipId is the one ID rule
(Validate and create), refusing Windows device names and a trailing dot, naming no value.
The add-on's one preference is Game folder; _aetheria_repo, the repo preference and the
dotnet-run invocation are deleted. Smokes take SMOKE_GAME_FOLDER. Not yet built or tested
at this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:53:03 +02:00
Meta Vens
7f64e0d733 Merge origin/master into ships-addon-frame-r4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:50:14 +02:00
Meta Vens
07dcb580c3 test_hull_grid: pin point-in-triangle sides, sample positions, bound samples, single triangles and messages
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:48:32 +02:00
Meta Vens
a9ce0bbc42 test_hull_grid: load hull_grid under its repo-rooted dotted name so mutmut can attribute kills
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:43:15 +02:00
Meta Vens
a308a7d8ea Add-on: New Ship, Flip Nose, Rasterise Hull and a Grid object; Source and Generated never exported
New Ship creates the ship .cc, binds through the one shared binding, moves the meshes into Source
under a Ship Root, turns them nose to -Y and sizes them. Rasterise is the only writer of
aetheria.grid_origin; Load, Save and Rasterise redraw the Grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:35:47 +02:00
Meta Vens
922928ebe7 Ship grid: hull_grid.py holds the cell arithmetic and the silhouette rasteriser, importable without Blender
cell_centre and cell_at map a cell to and from the Ship Root frame through a stored grid_origin;
rasterise turns top-down triangles into column-major cells (4x4 samples, half occupies, 32 cells
per axis at most).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:35:46 +02:00
Meta Vens
2cfdc72853 Delete the aether drive: behaviour, union 12, hardpoint type, aggregates, settings, presenters, prefab
AetherDriveData and AetherDrive, Union(12) (now a retired-do-not-reuse comment, as 26 is),
HardpointType.AetherDrive (last member, nothing shifts), Ship's drive sets, aggregates and
Axis write, the five request-field rows, GameplaySettings Aether multipliers, ShipInstance's
drive particles, SchematicDisplay's RPM display and AetherDriveUi, the undock refusal's drive
clause, the drive prefab and material, and AetherDb's drive roles and brownout row. The
design is kept at parked/aether-drive.
Adds EveryShippedShipHullHasATopSpeed. ARPG.unity still holds stale serialized references to
the removed SchematicDisplay fields and the RPM Display object; the next commit removes them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:04:28 +02:00
Meta Vens
7109ce9e57 retire-longinusx command: import its serializer namespace; keep the Python enum names equal to the enum until the enum member goes
The previous commit did not compile AetherDb (missing using) and dropped the
Python name while HardpointType.AetherDrive still existed, which ShipSchemaPinTests
pins. Both repaired; the name leaves with the enum member.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 13:55:55 +02:00
Meta Vens
bf17314765 Move every LonginusX consumer to the Longinus: scenarios, tests, tools, the starting hull
Duel, Arcs, Launcher Angles and Starved Reactor fit and place the Longinus
(thrusters, Core Power or MoveOnPro at (2,4), radiators relaid); the modes start
a Longinus (Settings.asset StartingHullName; the Aether multipliers leave it).
The drive's tests, its Steering Axis probe and its Brownout fixtures go with their
subject; ship_cc.py and the live-game checklist drop the drive and the hull.
The drive itself and the catalog records are deleted in the following commits.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 13:44:48 +02:00
Meta Vens
4895c75236 Bind Ship Collection: compute the stored .cc path before binding, absolute when relative cannot hold
The bind called bpy.path.relpath after setting aetheria.asset_kind and aetheria.id, so a .cc on another drive than the
.blend raised and left the collection half-bound, and an unsaved .blend stored a path relative to the working
directory. The stored path is now computed first: absolute for an unsaved .blend, '//'-relative when relpath succeeds,
absolute when it raises ValueError. Every reader already resolves both through bpy.path.abspath.

smoke_package.py gains the bind cases: unsaved stores absolute and survives two Save As moves, a failing bind leaves
no aetheria.* property, same-drive stores '//'-relative, and with SMOKE_OTHER_DRIVE the final bind crosses drives
before the package passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:37:21 +02:00
Meta Vens
7d649d5c31 Package Ship: headless Blender smoke, and the authoring doc says how Package and create --like are used
smoke_package.py builds a primitive ship from role-tagged objects, creates it --like Djinni, saves a layout with an
internal reactor, packages it and requires validate to pass; then swaps the thruster disc for an empty and requires the
refusal naming the anchor; then restores and packages again so the left-behind package is good. Proof gate 2's
outstanding authoring action is now Package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:13:44 +02:00
Meta Vens
82ca9e9be1 aetheria_ships: Package Ship derives anchors and ship.glb from the bound collection and runs AetherDb validate
Every object tagged aetheria.role becomes a ShipAnchor whose Id and ModelNodeId are its aetheria.id (a muzzle's ParentId
is its parent's). The collection, Grease Pencil excluded, exports to ship.glb with custom properties as node extras;
replace_visual writes the anchors, a lone Grease Pencil object's lines are captured, and AetherDb's verdict shows in the
panel. The add-on preference 'Aetheria repo' names the checkout; by default it is the nearest folder above the .cc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:13:42 +02:00
Meta Vens
aca1d53e6f ship_cc: replace_visual writes ShipAuthoring's model asset and anchors, pinned to the C# keys
MODEL_ASSET_SLOT, ANCHORS_SLOT and ANCHOR_MEMBERS sit beside the other slot constants and ShipSchemaPinTests binds them
to ShipAuthoring's and ShipAnchor's [Key]s. replace_visual carries an anchor's slots past Order by Id, refuses a blank
or duplicate id, a wrong-length row and a changed ship id, and leaves the hull record and the lines alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 17:07:10 +02:00