Skip to content

feat: edit plate contours vertex by vertex (A12.3) - #751

Merged
Blogbotana merged 1 commit into
Apibim-com:masterfrom
axisrow:ao/fork-sync-upstream/upstream-a12-3-solo
Oct 30, 2026
Merged

Blogbotana merged 1 commit into
Apibim-com:masterfrom
axisrow:ao/fork-sync-upstream/upstream-a12-3-solo

Conversation

@axisrow

@axisrow axisrow commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

What this changes

A12.3, plate contour editing commands: the five contour-editing commands of the accepted F04
plate amendment (section 3), on top of the already-delivered atomic --corners replacement
(#281). This is the remainder of the A12.3 work — the prerequisite slice landed, the editing
commands below did not (#520 was closed at the prerequisite).

  • Engine (beamcraft-engine::contour_edit, new): pure functions move_vertex,
    move_edge, insert_vertex, delete_vertex, set_corner — each maps the authored contour
    and its sparse corner list to the edited pair. Insertions and deletions remap treatment
    positions; the deleted vertex's treatment goes with it; an inserted vertex is sharp;
    set_corner replaces, clears (sharp/none) or inserts one treatment in vertex order.
    Indices are admitted by position (decision O-1); points are admitted finite and within the
    coordinate cap; a three-vertex contour is never reduced (INVALID_ARGUMENT, field
    contour_points_mm).
  • Model: ElementPatch.contour_points_mm — a complete contour replacement, validated like a
    created contour (count, finiteness, caps) and fingerprinted append-only
    (A12.3/contour-patch/v1), so fingerprints of earlier patches are unchanged.
  • Adapter (edit.rs): a contour patch is a shape patch; it is authored as Change::Contour
    on the canonical definition (next to Thickness/Corners) and applied to the linked
    definition group in the same revision.
  • CLI (move-plate-vertex, move-plate-edge, insert-plate-vertex, delete-plate-vertex,
    set-plate-corner): each reads the head plate, applies the engine edit and commits through
    the existing update_element path — so the kernel revalidates the new contour (winding,
    simplicity, holes, cuts, treated corners), a linked group follows, and each edit is one
    revision and one undo unit. Retry follows the documented UpdateElement contract, decided
    from the head state. Help text updated.

keep_degenerate/recovery (P-Degenerate) remains outstanding: invalid geometry refuses, as for
every update today. No UI acceptance is claimed (V18 calls these commands).

Design note: docs/design/a12-3-corner-edits.md.

How you verified it

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo nextest run --workspace (1909 passed on this branch)
  • cargo test --workspace --doc, wasm32 check of the pure crates — all green

New behaviour is tested: engine unit tests (index and finiteness refusals, treatment
carry-over, index remapping after insert/delete, triangle floor, set/clear/replace ordering),
update.rs tests (a contour patch is a shape change, validates the contour+corners pair, a
member refuses the field) and the e2e suite plate_contour_edits.rs (real binary): amendment
case 6 (delete gives a triangle, insert then move gives a pentagon, an edge move changes
exactly its two end vertices, closed-form volumes), treatment carry-over and remapping over
edits, negative cases 3–6 (collinear corner, delete on a triangle, self-crossing bowtie, hole
left outside) plus malformed/out-of-range/non-finite arguments and a member target — each
refusal leaves the store byte-identical; linked instance group updates atomically; immediate
retry replays; op-id reuse refuses; undo/redo round-trips.

Never-crash

  • No new unwrap/expect/panic!/todo! outside tests
  • No indexing or slicing with input-derived positions; allocations sized by input are capped
  • No unsafe

Provenance

  • Nothing taken from Tekla Structures or any Trimble product (assets, code, documentation, tables)
  • No GPL/AGPL code copied in
  • No new assets, so no ATTRIBUTION.md entries

Known risks / follow-ups

  • V18 grips (UI) are a separate card and call these commands.
  • P-Degenerate (keep_degenerate) will extend these commands when it lands.
  • Insert/delete retries under a moved-on accepted op id refuse as OP_ID_REUSE — the
    documented update-element family behaviour, kept here on purpose.

Anything a reviewer should look at first

The contour patch is authored as one complete replacement (not a delta) so the append-only
fingerprint chain of earlier patches never changes — worth a second look against the
UpdateElement replay contract.

@axisrow
axisrow requested a review from Blogbotana as a code owner October 30, 2026 17:24
Blogbotana
Blogbotana previously approved these changes Oct 30, 2026
* feat: edit plate contours vertex by vertex (A12.3)

The A12.3 prerequisite (atomic --corners replacement) could only retype the
whole sparse list; editing a contour after creation meant rewriting it from
scratch. This delivers the amendment's five editing commands on top of that
replacement: move-plate-vertex, move-plate-edge, insert-plate-vertex,
delete-plate-vertex and set-plate-corner.

The engine part is the pure contour_edit module: each edit maps the authored
contour and its sparse corner list to the edited pair, remapping treatment
positions after an insertion or deletion and carrying the deleted vertex's
treatment away. UpdateElement grows a complete contour_points_mm replacement
(fingerprinted append-only, so earlier patch fingerprints are unchanged); the
CLI commits each edit as one shape-changing contour + corners patch through
the existing update path, so the kernel revalidates the new contour, every
hole and cut replays, a linked definition group updates atomically and the
revision is one undo unit. delete-plate-vertex refuses on a three-vertex
contour whatever the caller asks; invalid geometry refuses before publication.
keep_degenerate (P-Degenerate) remains outstanding.

* fix: separate the contour-edits help segment from the next one

Completes the plate-contour editing remainder of #520.

Co-authored-by: axisrow <[email protected]>
@axisrow
axisrow force-pushed the ao/fork-sync-upstream/upstream-a12-3-solo branch from 04cefe0 to 73fd64d Compare October 30, 2026 20:30
@Blogbotana
Blogbotana merged commit 612b274 into Apibim-com:master Oct 30, 2026
24 checks passed
axisrow added a commit to axisrow/BeamCraft that referenced this pull request Oct 31, 2026
…d the wave-one registry integration (#345)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes (Apibim-com#748)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes

Typed user attributes (I04): attribute definitions and values on any element
across model, engine, layer engine and CLI, stored in USD, unknown attributes
never dropped. Expanded Q01 alpha story: a real-binary end-to-end scenario with
its coverage matrix. C03-C06 acceptance rows marked executable with the
corrected C04-09 cap; the document arrives in its fork state, overlapping the
open C03/C06 pull request that carries its new coordinator and compensation
tests.

* bug: reject control characters in an attribute-set name

AttrDef::validate checked only the length of a set name, the one free-text
field admitted without the label rule, so a raw newline or other control byte
could ride into the USDA string through bim:attr:set. The check now refuses
bytes up to U+001F and DEL, like unit labels, option strings and text values,
and the design card and the adversarial suite say so.

---------

Co-authored-by: axisrow <[email protected]>

* feat: rebase the workspace draft through the coordinator (C03) (Apibim-com#749)

The coordinator wire gains a `rebase` operation and the CLI a
`coordinator-rebase` client, so a client no longer needs a separate local
`rebase-workspace` process between publications: the same guarded local
rebase runs server-side with the token-derived actor as the only trusted
actor, never moves the published head, and replays one request identity
across both entry points and crash windows. Acceptance C03-R01..C03-R08
runs against a real `serve-coordinator` process with real token files
(`apps/beamcraft-cli/tests/coordinator_rebase.rs`).

The transport needed a host-side conversion from the validated model id of
the authenticated actor. It deliberately takes the validated
`beamcraft_model::id::Uuid7` instead of raw bytes: a constructor from
unchecked bytes would hand out a `Uuid7` that `Uuid7::parse` rejects and
break the invariant every persisted id relies on (review finding on the
combined C03/C06 delivery, Apibim-com#737). A round-trip unit
test pins the conversion.

Compensation (C06) is not part of this change: per that review,
compensating a published version is an administrator-only action that
needs the post-MVP roles-and-permissions model, so it stays deferred.

Co-authored-by: axisrow <[email protected]>

* refactor: add a single CLI command registry and a `commands` listing (Apibim-com#754)

* feat: schedule the verified head through attribute templates (I05) (Apibim-com#750)

* docs: design I05 schedules on the shared BOM and filter layers

* feat: schedule the verified head through attribute templates (I05)

Schedules by attributes: a declarative schedule template renders the verified
head — the same read `export-bom` uses — through columns from standard report
fields and typed user attributes (I04), one optional element filter, grouping
and totals, into CSV and HTML. The engine part is pure computation (template
and typed neutral CellValue rows in, sorted element table and group table out,
CSV and HTML rendering, the filter evaluator), the CLI decodes the template
against the pinned I04 attribute state and `schedule-export` writes the files
with the E03 atomic discipline. Unknown and stale values are shown, never
zeroed or dropped; identical inputs render byte-identical files.

* fix: quote schedule CSV headers and bound the template read

Fixes #532

Co-authored-by: axisrow <[email protected]>

* feat: edit plate contours vertex by vertex (A12.3) (Apibim-com#751)

* feat: build fastener geometry prototypes (A14.2) (Apibim-com#752)

* feat: build fastener geometry prototypes (A14.2)

One fastener prototype from a pinned FastenerSpec — head, shank, optional nut
and washers as separate closed meshes, hex and round polygons to the 0.05 mm
chord rule (vertex count a multiple of four), no threads, no chamfers — plus
per-component counts, volumes and mass with its source shown for a group of n
fasteners. beamcraft_kernel::fastener builds the components in stack order
from the bearing face as sealed G05 extrusions with exact volumes, mesh
digests and chord-rule provenance; the definition is re-admitted and refused
builds produce nothing. beamcraft_engine::fastener maps kernel refusals to
command fields and computes the group quantities in the P-Mass pattern (a
declared mass is never split, per-component masses are never invented, an
unknown density stays unknown). The headless `fastener-geometry` command reads
the library, never a store, and prints the component rows and group totals
with `mass_source`. The geometry is derived, nothing new is stored:
SCHEMA_DESCRIPTOR is untouched and A14.1 stores stay valid.

* fix: count the shank-through-head overlap once and keep the profile codes

Fixes #522

Co-authored-by: axisrow <[email protected]>

* Give a new model its environment so beams and columns can be placed at once (Apibim-com#755)

A new model had no profile or material definitions and the window only
lists the model's definitions, so no beam or column could be placed. The
environment chosen on the start window was only a saved setting.

- Add the built-in `starter` environment (nineteen generic, invented
  profiles) and prefer it on the start window over the empty `blank`.
- `create-project --environment ID` copies the environment into the
  project folder and publishes two default materials, so the project
  carries its environment and needs no option or setting afterwards.
- `--profile NAME` of create-member, create-polybeam and
  create-curved-beam defines the profile from the project's environment
  on first use; `definitions` lists the rest as `environment_profiles`
  and the window offers them in the profile picker.
- Allow an exactly zero density: `Steel_Undefined` is the massless
  material of auxiliary parts with a known mass of 0 kg, unlike an unknown
  density. Negative densities and underflowing products are still refused.

* Amend F04 and F06: a material density may be zero (Apibim-com#756)

* Let a placed member change its profile, in the CLI and in the window (Apibim-com#757)

A member's profile could only be chosen when it was created, and the
properties panel showed it as text, so a wrong section meant placing the
member again.

- ElementPatch gains a profile; the engine treats it as a shape change,
  rebuilds the body and the mass, and refuses it on a plate.
- update-element takes --profile NAME or --profile-digest; a name is
  resolved in the model and then in the project's environment, as for
  create-member. The existing layer-engine profile change repins the
  canonical part, so parts that share a definition follow it.
- The properties panel offers the model's and the environment's profiles
  for a beam or column and applies the choice with update-element.

* refactor: dispatch CLI commands from the registry and list access and parameters (slices 2-3 of Apibim-com#753) (Apibim-com#760)

* refactor: dispatch CLI commands from the registry (second slice of Apibim-com#753)

The binary chose an adapter with a chain of `c == "..."` comparisons in
main.rs plus a `handles()` function per adapter, a second hand-kept
inventory that could drift from the registry.

Each registry entry now names its adapter (`Route`), and main.rs matches
on `registry::route(word)`. A word that is not registered cannot reach an
adapter and takes the store-level path, which refuses it as before, so a
command that is dispatched but unregistered is no longer possible. The
per-adapter `handles()` functions are removed.

Behaviour, exit codes and output of existing commands are unchanged.

* test: wait for the dropped rebase request before the retry

The coordinator answers each connection on its own thread, so in
lost_answers_and_a_killed_server_replay_exactly_once the retry could
overtake the request whose client was dropped and do the merge itself,
answering workspace_rebased instead of workspace_rebase_replayed. It failed
once on the ubuntu CI runner.

Wait until the draft head moves, which happens when the dropped request has
merged, before sending the retry.

* refactor: list access and parameters of every CLI command (third slice of Apibim-com#753)

`beamcraft commands` now carries, for every command, whether it reads or
writes (Access) and its options: value shape, whether it is required and,
for alternatives, a one_of group. The five common request identity options
are listed on the commands that take them (Ids::Common) instead of being
repeated in 125 entries.

The data was taken from each adapter's allow-list and required checks, not
from the help prose; where they differ (for example update-element
--corners and --axis-point, create-grid, create-plate --material) the code
wins. A rule that links optional options (--width with --length) stays in
the family help.

Tests: the listing carries access and parameters; an unlisted option is
refused as unknown (so the probe is valid); every listed option is accepted
by its command, which fails if a listed name is wrong or removed.

---------

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: generate the CLI command reference from the registry (slice 7 of Apibim-com#753, first part) (Apibim-com#761)

COMMANDS.md renders every command from the registry as one usage line
(required options bare, optional ones in brackets, one-of groups in
parentheses) with its family, tier and access. A test fails when the
committed file differs from the generated text, and BEAMCRAFT_BLESS=1
rewrites it, so the reference cannot drift from `beamcraft commands`.

The README is not shortened yet: it carries rules and examples that the
registry does not hold.

Also lists three options the adapters accept but the registry missed:
update-element --profile and --profile-digest (added by Apibim-com#757) and
create-project --environment (added by Apibim-com#755). The "listed option is
accepted" test cannot see an omission, so every undeclared sibling option
was probed once by hand; no other command accepts an unlisted option except
the shared drawing allow-list and a few options that are accepted and
ignored or refused later.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: interactive-assist features need no CLI command (Apibim-com#762)

An agent builds a model by giving coordinates and ids directly, so cursor
picking and snapping and the start screen (settings, environments,
project-name suggestion, recent projects) do not need a CLI command. Narrow
the headless-first rule in AGENTS.md section 3 to features an agent or
script needs, and record the decision. The CLI commands themselves are
removed in a separate change; the engine APIs stay for the window.

Co-authored-by: Yury Isachenkov <[email protected]>

* refactor: remove the interactive-assist CLI commands and the tier (slices 4-5 of Apibim-com#753) (Apibim-com#764)

An agent builds a model by giving coordinates and ids directly, so the CLI
no longer needs cursor picking, snapping or the start screen. Remove pick,
snap, show-settings, set-settings, list-environment, suggest-project-name,
list-recent and add-recent (owner decision of 2026-10-31, see AGENTS.md
section 3). The engine and host functions behind them stay. list-feature,
lookup-feature, element-edges and work-plane stay: preview-drawing takes a
feature id.

With no ui-assist command left, the tier is empty information: drop
`Tier`, the `tier` field of `commands` and `commands --tier`.

The window still runs pick and the settings commands as a child process, so
its start screen, theme persistence and click selection are broken until
Apibim-com#763 moves it to the engine; the six UI tests that need them are ignored with
that issue's number.

The CLI tests of the removed commands are deleted (construction_snaps.rs,
the pick and snap tests, the settings tests) or cut down to the commands
that remain. The agent skill and the design notes no longer present them.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: point the CLI README at the generated command reference (slice 7 of Apibim-com#753) (Apibim-com#765)

The README repeated a usage block for most commands. They had drifted
(create-plate and update-element lacked many options the commands take) and
the generated COMMANDS.md now states them from the registry, with a test
that keeps it current. Replace each usage block with a link to the command's
entry there; the rules, examples and acceptance notes of each section stay.
The one worked example (define-profile then create-member with concrete
values) stays too.

Co-authored-by: Yury Isachenkov <[email protected]>

* Keep the fork's pick and snap commands through the sync: T02's acceptance and the window drive them, so the fork's copy of the registry contract test drops their removal spot-check

* Document the snap construction and grid switches and restore the construction snap acceptance suite the registry test spot-check had buried

* Regenerate the command reference with the annotation entries

---------

Co-authored-by: axisrow <[email protected]>
Co-authored-by: Yuri Isachenkov <[email protected]>
Co-authored-by: Yury Isachenkov <[email protected]>
axisrow added a commit to axisrow/BeamCraft that referenced this pull request Oct 31, 2026
… the #345 squash (#348)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes (Apibim-com#748)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes

Typed user attributes (I04): attribute definitions and values on any element
across model, engine, layer engine and CLI, stored in USD, unknown attributes
never dropped. Expanded Q01 alpha story: a real-binary end-to-end scenario with
its coverage matrix. C03-C06 acceptance rows marked executable with the
corrected C04-09 cap; the document arrives in its fork state, overlapping the
open C03/C06 pull request that carries its new coordinator and compensation
tests.

* bug: reject control characters in an attribute-set name

AttrDef::validate checked only the length of a set name, the one free-text
field admitted without the label rule, so a raw newline or other control byte
could ride into the USDA string through bim:attr:set. The check now refuses
bytes up to U+001F and DEL, like unit labels, option strings and text values,
and the design card and the adversarial suite say so.

---------

Co-authored-by: axisrow <[email protected]>

* feat: rebase the workspace draft through the coordinator (C03) (Apibim-com#749)

The coordinator wire gains a `rebase` operation and the CLI a
`coordinator-rebase` client, so a client no longer needs a separate local
`rebase-workspace` process between publications: the same guarded local
rebase runs server-side with the token-derived actor as the only trusted
actor, never moves the published head, and replays one request identity
across both entry points and crash windows. Acceptance C03-R01..C03-R08
runs against a real `serve-coordinator` process with real token files
(`apps/beamcraft-cli/tests/coordinator_rebase.rs`).

The transport needed a host-side conversion from the validated model id of
the authenticated actor. It deliberately takes the validated
`beamcraft_model::id::Uuid7` instead of raw bytes: a constructor from
unchecked bytes would hand out a `Uuid7` that `Uuid7::parse` rejects and
break the invariant every persisted id relies on (review finding on the
combined C03/C06 delivery, Apibim-com#737). A round-trip unit
test pins the conversion.

Compensation (C06) is not part of this change: per that review,
compensating a published version is an administrator-only action that
needs the post-MVP roles-and-permissions model, so it stays deferred.

Co-authored-by: axisrow <[email protected]>

* refactor: add a single CLI command registry and a `commands` listing (Apibim-com#754)

* feat: schedule the verified head through attribute templates (I05) (Apibim-com#750)

* docs: design I05 schedules on the shared BOM and filter layers

* feat: schedule the verified head through attribute templates (I05)

Schedules by attributes: a declarative schedule template renders the verified
head — the same read `export-bom` uses — through columns from standard report
fields and typed user attributes (I04), one optional element filter, grouping
and totals, into CSV and HTML. The engine part is pure computation (template
and typed neutral CellValue rows in, sorted element table and group table out,
CSV and HTML rendering, the filter evaluator), the CLI decodes the template
against the pinned I04 attribute state and `schedule-export` writes the files
with the E03 atomic discipline. Unknown and stale values are shown, never
zeroed or dropped; identical inputs render byte-identical files.

* fix: quote schedule CSV headers and bound the template read

Fixes #532

Co-authored-by: axisrow <[email protected]>

* feat: edit plate contours vertex by vertex (A12.3) (Apibim-com#751)

* feat: build fastener geometry prototypes (A14.2) (Apibim-com#752)

* feat: build fastener geometry prototypes (A14.2)

One fastener prototype from a pinned FastenerSpec — head, shank, optional nut
and washers as separate closed meshes, hex and round polygons to the 0.05 mm
chord rule (vertex count a multiple of four), no threads, no chamfers — plus
per-component counts, volumes and mass with its source shown for a group of n
fasteners. beamcraft_kernel::fastener builds the components in stack order
from the bearing face as sealed G05 extrusions with exact volumes, mesh
digests and chord-rule provenance; the definition is re-admitted and refused
builds produce nothing. beamcraft_engine::fastener maps kernel refusals to
command fields and computes the group quantities in the P-Mass pattern (a
declared mass is never split, per-component masses are never invented, an
unknown density stays unknown). The headless `fastener-geometry` command reads
the library, never a store, and prints the component rows and group totals
with `mass_source`. The geometry is derived, nothing new is stored:
SCHEMA_DESCRIPTOR is untouched and A14.1 stores stay valid.

* fix: count the shank-through-head overlap once and keep the profile codes

Fixes #522

Co-authored-by: axisrow <[email protected]>

* Give a new model its environment so beams and columns can be placed at once (Apibim-com#755)

A new model had no profile or material definitions and the window only
lists the model's definitions, so no beam or column could be placed. The
environment chosen on the start window was only a saved setting.

- Add the built-in `starter` environment (nineteen generic, invented
  profiles) and prefer it on the start window over the empty `blank`.
- `create-project --environment ID` copies the environment into the
  project folder and publishes two default materials, so the project
  carries its environment and needs no option or setting afterwards.
- `--profile NAME` of create-member, create-polybeam and
  create-curved-beam defines the profile from the project's environment
  on first use; `definitions` lists the rest as `environment_profiles`
  and the window offers them in the profile picker.
- Allow an exactly zero density: `Steel_Undefined` is the massless
  material of auxiliary parts with a known mass of 0 kg, unlike an unknown
  density. Negative densities and underflowing products are still refused.

* Amend F04 and F06: a material density may be zero (Apibim-com#756)

* Let a placed member change its profile, in the CLI and in the window (Apibim-com#757)

A member's profile could only be chosen when it was created, and the
properties panel showed it as text, so a wrong section meant placing the
member again.

- ElementPatch gains a profile; the engine treats it as a shape change,
  rebuilds the body and the mass, and refuses it on a plate.
- update-element takes --profile NAME or --profile-digest; a name is
  resolved in the model and then in the project's environment, as for
  create-member. The existing layer-engine profile change repins the
  canonical part, so parts that share a definition follow it.
- The properties panel offers the model's and the environment's profiles
  for a beam or column and applies the choice with update-element.

* refactor: dispatch CLI commands from the registry and list access and parameters (slices 2-3 of Apibim-com#753) (Apibim-com#760)

* refactor: dispatch CLI commands from the registry (second slice of Apibim-com#753)

The binary chose an adapter with a chain of `c == "..."` comparisons in
main.rs plus a `handles()` function per adapter, a second hand-kept
inventory that could drift from the registry.

Each registry entry now names its adapter (`Route`), and main.rs matches
on `registry::route(word)`. A word that is not registered cannot reach an
adapter and takes the store-level path, which refuses it as before, so a
command that is dispatched but unregistered is no longer possible. The
per-adapter `handles()` functions are removed.

Behaviour, exit codes and output of existing commands are unchanged.

* test: wait for the dropped rebase request before the retry

The coordinator answers each connection on its own thread, so in
lost_answers_and_a_killed_server_replay_exactly_once the retry could
overtake the request whose client was dropped and do the merge itself,
answering workspace_rebased instead of workspace_rebase_replayed. It failed
once on the ubuntu CI runner.

Wait until the draft head moves, which happens when the dropped request has
merged, before sending the retry.

* refactor: list access and parameters of every CLI command (third slice of Apibim-com#753)

`beamcraft commands` now carries, for every command, whether it reads or
writes (Access) and its options: value shape, whether it is required and,
for alternatives, a one_of group. The five common request identity options
are listed on the commands that take them (Ids::Common) instead of being
repeated in 125 entries.

The data was taken from each adapter's allow-list and required checks, not
from the help prose; where they differ (for example update-element
--corners and --axis-point, create-grid, create-plate --material) the code
wins. A rule that links optional options (--width with --length) stays in
the family help.

Tests: the listing carries access and parameters; an unlisted option is
refused as unknown (so the probe is valid); every listed option is accepted
by its command, which fails if a listed name is wrong or removed.

---------

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: generate the CLI command reference from the registry (slice 7 of Apibim-com#753, first part) (Apibim-com#761)

COMMANDS.md renders every command from the registry as one usage line
(required options bare, optional ones in brackets, one-of groups in
parentheses) with its family, tier and access. A test fails when the
committed file differs from the generated text, and BEAMCRAFT_BLESS=1
rewrites it, so the reference cannot drift from `beamcraft commands`.

The README is not shortened yet: it carries rules and examples that the
registry does not hold.

Also lists three options the adapters accept but the registry missed:
update-element --profile and --profile-digest (added by Apibim-com#757) and
create-project --environment (added by Apibim-com#755). The "listed option is
accepted" test cannot see an omission, so every undeclared sibling option
was probed once by hand; no other command accepts an unlisted option except
the shared drawing allow-list and a few options that are accepted and
ignored or refused later.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: interactive-assist features need no CLI command (Apibim-com#762)

An agent builds a model by giving coordinates and ids directly, so cursor
picking and snapping and the start screen (settings, environments,
project-name suggestion, recent projects) do not need a CLI command. Narrow
the headless-first rule in AGENTS.md section 3 to features an agent or
script needs, and record the decision. The CLI commands themselves are
removed in a separate change; the engine APIs stay for the window.

Co-authored-by: Yury Isachenkov <[email protected]>

* refactor: remove the interactive-assist CLI commands and the tier (slices 4-5 of Apibim-com#753) (Apibim-com#764)

An agent builds a model by giving coordinates and ids directly, so the CLI
no longer needs cursor picking, snapping or the start screen. Remove pick,
snap, show-settings, set-settings, list-environment, suggest-project-name,
list-recent and add-recent (owner decision of 2026-10-31, see AGENTS.md
section 3). The engine and host functions behind them stay. list-feature,
lookup-feature, element-edges and work-plane stay: preview-drawing takes a
feature id.

With no ui-assist command left, the tier is empty information: drop
`Tier`, the `tier` field of `commands` and `commands --tier`.

The window still runs pick and the settings commands as a child process, so
its start screen, theme persistence and click selection are broken until
Apibim-com#763 moves it to the engine; the six UI tests that need them are ignored with
that issue's number.

The CLI tests of the removed commands are deleted (construction_snaps.rs,
the pick and snap tests, the settings tests) or cut down to the commands
that remain. The agent skill and the design notes no longer present them.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: point the CLI README at the generated command reference (slice 7 of Apibim-com#753) (Apibim-com#765)

The README repeated a usage block for most commands. They had drifted
(create-plate and update-element lacked many options the commands take) and
the generated COMMANDS.md now states them from the registry, with a test
that keeps it current. Replace each usage block with a link to the command's
entry there; the rules, examples and acceptance notes of each section stay.
The one worked example (define-profile then create-member with concrete
values) stays too.

Co-authored-by: Yury Isachenkov <[email protected]>

---------

Co-authored-by: axisrow <[email protected]>
Co-authored-by: Yuri Isachenkov <[email protected]>
Co-authored-by: Yury Isachenkov <[email protected]>
axisrow added a commit to axisrow/BeamCraft that referenced this pull request Oct 31, 2026
…upstream Apibim-com#748-Apibim-com#765 (#356)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes (Apibim-com#748)

* feat: deliver I04 user attributes, the expanded Q01 alpha story and C04 acceptance fixes

Typed user attributes (I04): attribute definitions and values on any element
across model, engine, layer engine and CLI, stored in USD, unknown attributes
never dropped. Expanded Q01 alpha story: a real-binary end-to-end scenario with
its coverage matrix. C03-C06 acceptance rows marked executable with the
corrected C04-09 cap; the document arrives in its fork state, overlapping the
open C03/C06 pull request that carries its new coordinator and compensation
tests.

* bug: reject control characters in an attribute-set name

AttrDef::validate checked only the length of a set name, the one free-text
field admitted without the label rule, so a raw newline or other control byte
could ride into the USDA string through bim:attr:set. The check now refuses
bytes up to U+001F and DEL, like unit labels, option strings and text values,
and the design card and the adversarial suite say so.

---------

Co-authored-by: axisrow <[email protected]>

* feat: rebase the workspace draft through the coordinator (C03) (Apibim-com#749)

The coordinator wire gains a `rebase` operation and the CLI a
`coordinator-rebase` client, so a client no longer needs a separate local
`rebase-workspace` process between publications: the same guarded local
rebase runs server-side with the token-derived actor as the only trusted
actor, never moves the published head, and replays one request identity
across both entry points and crash windows. Acceptance C03-R01..C03-R08
runs against a real `serve-coordinator` process with real token files
(`apps/beamcraft-cli/tests/coordinator_rebase.rs`).

The transport needed a host-side conversion from the validated model id of
the authenticated actor. It deliberately takes the validated
`beamcraft_model::id::Uuid7` instead of raw bytes: a constructor from
unchecked bytes would hand out a `Uuid7` that `Uuid7::parse` rejects and
break the invariant every persisted id relies on (review finding on the
combined C03/C06 delivery, Apibim-com#737). A round-trip unit
test pins the conversion.

Compensation (C06) is not part of this change: per that review,
compensating a published version is an administrator-only action that
needs the post-MVP roles-and-permissions model, so it stays deferred.

Co-authored-by: axisrow <[email protected]>

* refactor: add a single CLI command registry and a `commands` listing (Apibim-com#754)

* feat: schedule the verified head through attribute templates (I05) (Apibim-com#750)

* docs: design I05 schedules on the shared BOM and filter layers

* feat: schedule the verified head through attribute templates (I05)

Schedules by attributes: a declarative schedule template renders the verified
head — the same read `export-bom` uses — through columns from standard report
fields and typed user attributes (I04), one optional element filter, grouping
and totals, into CSV and HTML. The engine part is pure computation (template
and typed neutral CellValue rows in, sorted element table and group table out,
CSV and HTML rendering, the filter evaluator), the CLI decodes the template
against the pinned I04 attribute state and `schedule-export` writes the files
with the E03 atomic discipline. Unknown and stale values are shown, never
zeroed or dropped; identical inputs render byte-identical files.

* fix: quote schedule CSV headers and bound the template read

Fixes #532

Co-authored-by: axisrow <[email protected]>

* feat: edit plate contours vertex by vertex (A12.3) (Apibim-com#751)

* feat: build fastener geometry prototypes (A14.2) (Apibim-com#752)

* feat: build fastener geometry prototypes (A14.2)

One fastener prototype from a pinned FastenerSpec — head, shank, optional nut
and washers as separate closed meshes, hex and round polygons to the 0.05 mm
chord rule (vertex count a multiple of four), no threads, no chamfers — plus
per-component counts, volumes and mass with its source shown for a group of n
fasteners. beamcraft_kernel::fastener builds the components in stack order
from the bearing face as sealed G05 extrusions with exact volumes, mesh
digests and chord-rule provenance; the definition is re-admitted and refused
builds produce nothing. beamcraft_engine::fastener maps kernel refusals to
command fields and computes the group quantities in the P-Mass pattern (a
declared mass is never split, per-component masses are never invented, an
unknown density stays unknown). The headless `fastener-geometry` command reads
the library, never a store, and prints the component rows and group totals
with `mass_source`. The geometry is derived, nothing new is stored:
SCHEMA_DESCRIPTOR is untouched and A14.1 stores stay valid.

* fix: count the shank-through-head overlap once and keep the profile codes

Fixes #522

Co-authored-by: axisrow <[email protected]>

* Give a new model its environment so beams and columns can be placed at once (Apibim-com#755)

A new model had no profile or material definitions and the window only
lists the model's definitions, so no beam or column could be placed. The
environment chosen on the start window was only a saved setting.

- Add the built-in `starter` environment (nineteen generic, invented
  profiles) and prefer it on the start window over the empty `blank`.
- `create-project --environment ID` copies the environment into the
  project folder and publishes two default materials, so the project
  carries its environment and needs no option or setting afterwards.
- `--profile NAME` of create-member, create-polybeam and
  create-curved-beam defines the profile from the project's environment
  on first use; `definitions` lists the rest as `environment_profiles`
  and the window offers them in the profile picker.
- Allow an exactly zero density: `Steel_Undefined` is the massless
  material of auxiliary parts with a known mass of 0 kg, unlike an unknown
  density. Negative densities and underflowing products are still refused.

* Amend F04 and F06: a material density may be zero (Apibim-com#756)

* Let a placed member change its profile, in the CLI and in the window (Apibim-com#757)

A member's profile could only be chosen when it was created, and the
properties panel showed it as text, so a wrong section meant placing the
member again.

- ElementPatch gains a profile; the engine treats it as a shape change,
  rebuilds the body and the mass, and refuses it on a plate.
- update-element takes --profile NAME or --profile-digest; a name is
  resolved in the model and then in the project's environment, as for
  create-member. The existing layer-engine profile change repins the
  canonical part, so parts that share a definition follow it.
- The properties panel offers the model's and the environment's profiles
  for a beam or column and applies the choice with update-element.

* refactor: dispatch CLI commands from the registry and list access and parameters (slices 2-3 of Apibim-com#753) (Apibim-com#760)

* refactor: dispatch CLI commands from the registry (second slice of Apibim-com#753)

The binary chose an adapter with a chain of `c == "..."` comparisons in
main.rs plus a `handles()` function per adapter, a second hand-kept
inventory that could drift from the registry.

Each registry entry now names its adapter (`Route`), and main.rs matches
on `registry::route(word)`. A word that is not registered cannot reach an
adapter and takes the store-level path, which refuses it as before, so a
command that is dispatched but unregistered is no longer possible. The
per-adapter `handles()` functions are removed.

Behaviour, exit codes and output of existing commands are unchanged.

* test: wait for the dropped rebase request before the retry

The coordinator answers each connection on its own thread, so in
lost_answers_and_a_killed_server_replay_exactly_once the retry could
overtake the request whose client was dropped and do the merge itself,
answering workspace_rebased instead of workspace_rebase_replayed. It failed
once on the ubuntu CI runner.

Wait until the draft head moves, which happens when the dropped request has
merged, before sending the retry.

* refactor: list access and parameters of every CLI command (third slice of Apibim-com#753)

`beamcraft commands` now carries, for every command, whether it reads or
writes (Access) and its options: value shape, whether it is required and,
for alternatives, a one_of group. The five common request identity options
are listed on the commands that take them (Ids::Common) instead of being
repeated in 125 entries.

The data was taken from each adapter's allow-list and required checks, not
from the help prose; where they differ (for example update-element
--corners and --axis-point, create-grid, create-plate --material) the code
wins. A rule that links optional options (--width with --length) stays in
the family help.

Tests: the listing carries access and parameters; an unlisted option is
refused as unknown (so the probe is valid); every listed option is accepted
by its command, which fails if a listed name is wrong or removed.

---------

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: generate the CLI command reference from the registry (slice 7 of Apibim-com#753, first part) (Apibim-com#761)

COMMANDS.md renders every command from the registry as one usage line
(required options bare, optional ones in brackets, one-of groups in
parentheses) with its family, tier and access. A test fails when the
committed file differs from the generated text, and BEAMCRAFT_BLESS=1
rewrites it, so the reference cannot drift from `beamcraft commands`.

The README is not shortened yet: it carries rules and examples that the
registry does not hold.

Also lists three options the adapters accept but the registry missed:
update-element --profile and --profile-digest (added by Apibim-com#757) and
create-project --environment (added by Apibim-com#755). The "listed option is
accepted" test cannot see an omission, so every undeclared sibling option
was probed once by hand; no other command accepts an unlisted option except
the shared drawing allow-list and a few options that are accepted and
ignored or refused later.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: interactive-assist features need no CLI command (Apibim-com#762)

An agent builds a model by giving coordinates and ids directly, so cursor
picking and snapping and the start screen (settings, environments,
project-name suggestion, recent projects) do not need a CLI command. Narrow
the headless-first rule in AGENTS.md section 3 to features an agent or
script needs, and record the decision. The CLI commands themselves are
removed in a separate change; the engine APIs stay for the window.

Co-authored-by: Yury Isachenkov <[email protected]>

* refactor: remove the interactive-assist CLI commands and the tier (slices 4-5 of Apibim-com#753) (Apibim-com#764)

An agent builds a model by giving coordinates and ids directly, so the CLI
no longer needs cursor picking, snapping or the start screen. Remove pick,
snap, show-settings, set-settings, list-environment, suggest-project-name,
list-recent and add-recent (owner decision of 2026-10-31, see AGENTS.md
section 3). The engine and host functions behind them stay. list-feature,
lookup-feature, element-edges and work-plane stay: preview-drawing takes a
feature id.

With no ui-assist command left, the tier is empty information: drop
`Tier`, the `tier` field of `commands` and `commands --tier`.

The window still runs pick and the settings commands as a child process, so
its start screen, theme persistence and click selection are broken until
Apibim-com#763 moves it to the engine; the six UI tests that need them are ignored with
that issue's number.

The CLI tests of the removed commands are deleted (construction_snaps.rs,
the pick and snap tests, the settings tests) or cut down to the commands
that remain. The agent skill and the design notes no longer present them.

Co-authored-by: Yury Isachenkov <[email protected]>

* docs: point the CLI README at the generated command reference (slice 7 of Apibim-com#753) (Apibim-com#765)

The README repeated a usage block for most commands. They had drifted
(create-plate and update-element lacked many options the commands take) and
the generated COMMANDS.md now states them from the registry, with a test
that keeps it current. Replace each usage block with a link to the command's
entry there; the rules, examples and acceptance notes of each section stay.
The one worked example (define-profile then create-member with concrete
values) stays too.

Co-authored-by: Yury Isachenkov <[email protected]>

---------

Co-authored-by: axisrow <[email protected]>
Co-authored-by: Yuri Isachenkov <[email protected]>
Co-authored-by: Yury Isachenkov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants