Skip to content

Commit 9e3ce68

Browse files
committed
fix: resolve outstanding issues for v0.46.0 (#189)
2 parents 202069b + 34dea03 commit 9e3ce68

95 files changed

Lines changed: 4368 additions & 731 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# CLI migration and operator behavior
2+
3+
Apply Rust planner/author standards using existing domain and error types.
4+
5+
1. Plan every desired schema before apply/migrate execution and reject any destructive noninteractive batch before migration, metadata, or revision preparation writes. Preserve interactive per-schema consent and dry-run plans. Test mixed safe/destructive batches with zero writes.
6+
2. Preserve configured listener host/port with optional flags. Keep SchemaForge loopback as its unconfigured host, respecting the framework config search and ACTON environment layers. Test omitted flags, explicit default overrides, and config-file bind/port.
7+
3. Keep the existing public RequiresConfirmation enum variant for source compatibility, but label it review in Display/serialized output, accept legacy serialized spelling, and document it as informational consistently. Introduce plan-aware step classification for fresh unique constraints and test both existing/new schema uniqueness.
8+
4. Add bounded 429 retries to the entity HTTP client, with --max-retries and Retry-After seconds/date support. Rebuild identical requests only for explicit 429 responses, no transport or 5xx retries. Test exhaustion, eventual success, non-429 refusal, and delay parsing.
9+
5. Correct the rule-ordering reference and document governor defaults, reverse-proxy trust and probe configuration.
10+
6. Coordinate read-only CLI connections and webhook validation with owning agents.
11+
12+
Validation: cargo nextest run for core and CLI with postgres feature, cargo clippy warnings denied, formatting. Root performs workspace integration and release. Semver recommendation: minor because migration machine-readable review labels change and CLI functionality is added; retain deserialization compatibility.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Generated field types and authority, issues 191 and 192
2+
3+
Use the existing view-model and template architecture. Keep display types complete while filtering form authority separately.
4+
5+
- Add duration (Go-style duration string validation), bytes (base64 text with decoded size constraint), and map (typed Record JSON textarea) mapping. Preserve recursive composite/array type projection. Format duration wire strings into readable units without altering submitted values.
6+
- Project computed/read-role/write-role metadata on FieldView. Omit computed and derived fields from form controls and form validators. Use current auth roles at render/parse/submit time, not module initialization. Hide read-denied controls, render write-denied values inert, and ensure validation does not require denied fields.
7+
- Use recursive metadata to filter initial and submitted state, stripping read-denied, computed/derived and unwritable payload fields including nested composites. Preserve readable, write-denied initial values for read-only display. Normalize JSON and composite values recursively so nested supported fields remain round-trippable.
8+
- Update field-type and permissions documentation. Add generator regression checks plus Playwright behavior tests for serialization, validation and role changes. Fail generation for any remaining unsupported required field.
9+
- Preserve existing generated styling and accessibility labels, avoiding controls which imply unavailable actions (UI design expert, interaction patterns).
10+
11+
No new dependencies or error types required. Semver: fixes in release already planned by root. Run only targeted cargo check locally; root runs generation, TypeScript build/lint, and browser checks in CI. Sign conventional commits; no push.
12+
13+
## CI follow-up: exact browser metadata
14+
15+
Site CI reported TS2352 because full FieldView JSON contains display-only properties. Introduce a dedicated recursive FormFieldSpec serialization type; use it for all template metadata arguments and entity normalizer tables. Remove casts, preserve hidden-descendant/role flags, and test the exact serialized keys at every depth.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# UI Design Review
2+
3+
The generated forms imply users can edit values the server will discard. Apply Norman's affordance principle: omit computed inputs, hide unreadable fields, and render readable but unwritable values as inert text. Share gating across validation and payload construction so hidden required controls cannot block saving. Keep existing form styling and labels; provide duration and base64 format hints. Browser regression coverage should assert visible controls and actual submitted payloads for both permitted and denied roles.

‎.github/workflows/site-e2e.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,14 @@ jobs:
5656
crates/schema-forge-cli/tests/site_e2e/playwright/test-results
5757
if-no-files-found: ignore
5858
retention-days: 7
59+
60+
- name: Upload complete server logs on failure
61+
if: failure()
62+
uses: actions/upload-artifact@v7
63+
with:
64+
name: site-server-logs
65+
path: |
66+
target/site-e2e-*/backend.log
67+
target/site-e2e-*/vite.log
68+
if-no-files-found: ignore
69+
retention-days: 7

‎CHANGELOG.md‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,83 @@ is pre-1.0; breaking changes bump the **minor** version per
77

88
## [Unreleased]
99

10+
## [0.46.0] - 2026-09-25
11+
12+
### Runtime and API behavior
13+
14+
- Validate tenant declarations consistently across startup, CLI schema application,
15+
and runtime schema changes. Applications with a tenant root must annotate every
16+
application schema; built-in system schemas remain shared.
17+
- Validate relation targets against tenant scope and read authorization before
18+
create, PUT, or PATCH persists. Platform administrators retain their documented
19+
cross-tenant capabilities.
20+
- Apply hidden-field projection to relation display labels and webhook payloads.
21+
Webhooks use a fixed conservative field policy and plain JSON payload version 2.
22+
- Enforce configured webhook URL schemes and public destinations during
23+
configuration and delivery. Delivery checks and pins DNS results, with redirects
24+
and environment proxies disabled.
25+
- Reject undeclared entity fields before persistence. Foreign-key errors include
26+
machine-readable `error` and `message` fields; entity and schema JSON rejections
27+
use the API error envelope. Internal storage diagnostics stay out of REST and
28+
GraphQL error messages, and database connection errors omit credentials.
29+
- Keep authorization resource attributes aligned with the generated Cedar schema,
30+
so arrays of unsupported policy types do not incorrectly deny valid writes.
31+
- Convert nested composite values using their declared field types before storage.
32+
33+
### Database and operator fixes
34+
35+
- PostgreSQL planning and inspection connections perform no bookkeeping DDL.
36+
Fresh databases plan as empty registries, and read-only roles can inspect existing
37+
metadata without schema creation privileges.
38+
- PostgreSQL `contains` and `startswith` now match literal, case-sensitive text.
39+
Unsupported array filter comparisons return validation errors before execution.
40+
- `apply` and `migrate --execute` preflight all selected migration plans before
41+
applying a noninteractive batch. Refusals identify every destructive schema and
42+
step requiring `--force`.
43+
- Migration warnings display `review` when they are informational. New-table unique
44+
constraints are safe, and new schemas retain their `CREATE` label.
45+
- Explicit listener flags override environment and file settings; omitted flags
46+
preserve configuration. An unconfigured server defaults to `127.0.0.1:3000`.
47+
- Entity CLI requests retry HTTP 429 with `Retry-After` support and bounded fallback
48+
backoff, controlled by `--max-retries`. Transport failures are not retried.
49+
- Document governor quotas, proxy configuration, probe routes, the full write-rule
50+
order, and webhook delivery guarantees.
51+
52+
### Generated sites
53+
54+
- Carry the active tenant on entity, invitation, and file requests, including
55+
requests retried after a token refresh.
56+
- Show readable API errors and avoid duplicate global notifications when pages
57+
handle errors locally. Projects can customize the preserved error-toast helper.
58+
- Generate typed duration, map, and base64 bytes fields in forms, lists, and
59+
details. Form validation and payload normalization respect computed fields and
60+
role-based field access. Composites with protected children remain read-only.
61+
- Configure the product name, title suffix, and SVG logos and favicon through
62+
`[schema_forge.site]` or generation flags. Default marks are neutral, and CSS,
63+
title helpers, and SVG assets support template overrides and drift checking.
64+
- CI now builds and lints generated TypeScript before running browser tests.
65+
66+
### Upgrade notes
67+
68+
Webhook consumers must support `payload_version: 2` and plain JSON field values.
69+
Hidden fields and fields with field-access annotations are excluded. Webhooks
70+
remain best effort with no durable history or replay; applications must reconcile
71+
current state separately when delivery gaps matter. See [webhooks](docs/webhooks.md).
72+
73+
Before upgrading a tenanted deployment, annotate every application schema with its
74+
intended tenant relationship and migrate existing ownership explicitly. Unannotated
75+
application schemas are no longer implicitly shared when a tenant root exists.
76+
See [tenant isolation](docs/tenant-isolation.md).
77+
78+
Migration safety serialization emits `Review`; legacy `RequiresConfirmation` input
79+
is still accepted. Rust embedders must update webhook event constructor calls to
80+
pass schema definitions, and handler callers must use the new JSON extractor.
81+
Workspace crate versions are coordinated for the updated public core/backend types.
82+
83+
Regenerate sites to update owned API and branding helpers. Existing customized
84+
page shells remain preserved; see the migration instructions for
85+
[error feedback](docs/generated-site-errors.md) and [branding](docs/site-branding.md).
86+
1087
## [0.45.0] - 2026-09-24
1188

1289
### Security and correctness

‎Cargo.lock‎

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,3 +1056,7 @@ See the project repository for license information.
10561056
Platform administrators can browse recorded audit events and verify bounded chain ranges through the [audit API](docs/audit-api-reference.md). Access is deployment-wide, uses the active framework audit store, and reports collection limits separately from local chain consistency. Available in v0.42.0.
10571057

10581058
See [safe schema changes](docs/migrations/safe-schema-changes.md) for declared field renames, destructive migration opt-ins, PostgreSQL relation integrity, and explicit tenancy migrations.
1059+
1060+
See the [webhook delivery contract](docs/webhooks.md) for delivery guarantees, payload format, and destination policy.
1061+
1062+
Configure product names, title suffixes, and SVG marks with [generated-site branding](docs/site-branding.md).

‎SECURITY.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ Security fixes go into the latest release. Older versions do not receive backpor
3131

3232
| Version | Supported |
3333
|---|---|
34-
| 0.45.x (latest release) | Yes |
35-
| earlier than 0.45 | No, please upgrade |
34+
| 0.46.x (latest release) | Yes |
35+
| earlier than 0.46 | No, please upgrade |
3636

3737
## Scope
3838

‎crates/schema-forge-acton/Cargo.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "schema-forge-acton"
3-
version = "0.44.0"
3+
version = "0.45.0"
44
edition = "2021"
55

66
[dependencies]
@@ -44,7 +44,7 @@ aws-lc-rs = { version = "1", features = ["fips"], optional = true }
4444
rustls = { version = "0.23", default-features = false, features = ["std", "aws_lc_rs", "logging"] }
4545
schema-forge-signing = { version = "0.1.0", path = "../schema-forge-signing" }
4646
lettre = { version = "0.11.22", default-features = false, features = ["tokio1-rustls", "aws-lc-rs", "webpki-roots", "smtp-transport", "builder", "pool", "hostname"] }
47-
schema-forge-cel = { version = "0.11.0", path = "../schema-forge-cel" }
47+
schema-forge-cel = { version = "0.12.0", path = "../schema-forge-cel" }
4848
rust_xlsxwriter = { version = "0.95.0", features = ["chrono"] }
4949
zip = "8.6.0"
5050

‎crates/schema-forge-acton/src/authz/adapters.rs‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,13 @@ pub fn build_resource_entity(
220220

221221
let mut attrs: HashMap<String, RestrictedExpression> = HashMap::new();
222222
for (field_name, value) in &entity.fields {
223-
// `@hidden` fields are never declared as Cedar attributes, so
224-
// including them here would fail strict-mode entity validation.
225-
// The schema's field definition is the canonical source of the
226-
// hidden flag — entities loaded from storage may still carry the
227-
// value, but it must not leak into authorization context.
223+
// Only types declared by the schema generator may enter Cedar.
224+
// Unsupported arrays otherwise become undeclared set attributes,
225+
// causing strict validation to reject even permitted operations.
228226
if let Some(field_def) = schema.field(field_name) {
229-
if field_def.is_hidden() {
227+
if field_def.is_hidden()
228+
|| crate::cedar::schema_gen::cedar_type_for(&field_def.field_type).is_none()
229+
{
230230
continue;
231231
}
232232
// The Cedar schema declares file attributes as strings. Supply
@@ -304,10 +304,11 @@ pub fn build_resource_placeholder(schema: &SchemaDefinition) -> Result<CedarEnti
304304

305305
let mut attrs: HashMap<String, RestrictedExpression> = HashMap::new();
306306
for field in &schema.fields {
307-
if !field.is_required() || field.is_hidden() {
308-
// Hidden fields are not declared in the Cedar schema, so the
309-
// strict-mode entity validator would reject a placeholder that
310-
// includes them.
307+
if !field.is_required()
308+
|| field.is_hidden()
309+
|| crate::cedar::schema_gen::cedar_type_for(&field.field_type).is_none()
310+
{
311+
// Hidden and unsupported field types have no Cedar attribute.
311312
continue;
312313
}
313314
if let Some(expr) = default_cedar_expr(&field.field_type) {
@@ -378,7 +379,7 @@ pub fn dynamic_to_cedar(value: &DynamicValue) -> Option<RestrictedExpression> {
378379
}
379380
DynamicValue::Array(items) => {
380381
let mapped: Vec<RestrictedExpression> =
381-
items.iter().filter_map(dynamic_to_cedar).collect();
382+
items.iter().map(dynamic_to_cedar).collect::<Option<_>>()?;
382383
Some(RestrictedExpression::new_set(mapped))
383384
}
384385
DynamicValue::Null | DynamicValue::Json(_) | DynamicValue::Composite(_) => None,
@@ -424,6 +425,15 @@ mod principal_claim_tests {
424425
PrincipalClaimsConfig,
425426
};
426427

428+
#[test]
429+
fn array_projection_does_not_silently_drop_unrepresentable_members() {
430+
let value = DynamicValue::Array(vec![
431+
DynamicValue::Integer(1),
432+
DynamicValue::Json(serde_json::json!({"value": 2})),
433+
]);
434+
assert!(dynamic_to_cedar(&value).is_none());
435+
}
436+
427437
fn claims_with(custom: HashMap<String, serde_json::Value>) -> Claims {
428438
Claims {
429439
sub: "user:alice".into(),

0 commit comments

Comments
 (0)