Repository navigation
Normalize persisted render surfaces - #15
Conversation
📝 WalkthroughWalkthroughThe change adds canonical render-surface preflight and normalization for actions, storage, seeds, and rendering. It adds safe ChangesCanonical rendering pipeline
Estimated code review effort: 5 (Critical) | ~120 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (7)
aria/actions/nodes.ts (1)
71-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
collectionSurfaceKindis defined three times. This PR adds the same collection-to-RenderSurfaceKindmapping in three separate files. The shared root cause is the absence of one exported helper next to theRenderSurfaceKindcontract. If a fourth collection is added, or a mapping changes, all three copies must be updated together, and a missed copy produces a wrong surface kind in preflight.
aria/actions/nodes.ts#L71-L80: remove the local function and import the shared helper.aria/admin/features/Agent/lib/tools/content/nodeWriteTools.ts#L45-L51: remove the local function and import the shared helper.aria/admin/features/Agent/client/composables/useAgentClientTools.ts#L53-L59: remove the local function and import the shared helper.Export the helper from
aria/lib/rendering/canonical/contract.ts, whereRenderSurfaceKindis declared, and type its parameter as the sharedCollectionType.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/actions/nodes.ts` around lines 71 - 80, Centralize the collection-to-surface mapping by exporting a helper next to RenderSurfaceKind in aria/lib/rendering/canonical/contract.ts, with a CollectionType parameter. Remove the local collectionSurfaceKind definitions and import the shared helper in aria/actions/nodes.ts (lines 71-80), aria/admin/features/Agent/lib/tools/content/nodeWriteTools.ts (lines 45-51), and aria/admin/features/Agent/client/composables/useAgentClientTools.ts (lines 53-59).aria/lib/migrations/propMigrations.ts (1)
108-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider keeping structured logging on this path.
migratePageDSLnow runs insidenormalizeEditableSurface, so this branch executes on the persistence path.console.errordrops the stack trace and the structured context that the previous logger emitted. If the logger was removed for edge-runtime portability, state that reason in a short comment so the change is not reverted later.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/lib/migrations/propMigrations.ts` around lines 108 - 111, Update the migration error handling in migratePageDSL to preserve structured logging and the error stack/context previously emitted by the logger, rather than using console.error with only error.message. If console.error is required for edge-runtime portability, add a brief comment documenting that constraint.aria/lib/storage/internal/domains/dslAssets.ts (1)
187-232: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid recomputing
existingVersionHashtwice for the same row.In
saveLayoutDSL(Lines 193-198 and 212-217) andsaveComponentDSL(Lines 477-482 and 496-501),resolveStoredSemanticSourceHashruns twice against the sameexistingVersionRowwith identical inputs. The first call decidesshouldInsertVersion. The second call, insideif (!shouldInsertVersion && existingVersionRow), recomputes the exact same value to decide whether to run theUPDATE.Each call parses
dsl_jsonand re-normalizes the surface, so this doubles that cost on every save that reuses an existing version (pinned or overwrite saves). Cache the first result and reuse it.♻️ Proposed fix for the layout path (apply the same pattern to the component path)
let shouldInsertVersion = true; const existingVersionRow = await context.getStoredVersionRow( "aria_layout_versions", id, version, ); + let existingVersionHash: string | undefined; if (existingVersionRow) { - const existingVersionHash = await resolveStoredSemanticSourceHash({ + existingVersionHash = await resolveStoredSemanticSourceHash({ kind: "layout", row: existingVersionRow, fallback: () => context.resolveStoredVersionContentHash(existingVersionRow), }); if (existingVersionHash !== incomingHash) { if (!parsedOptions.overwriteVersionIfExists) { throw new Error( `Layout version conflict for ${id}: version ${version} already exists with different content`, ); } shouldInsertVersion = false; } else { shouldInsertVersion = false; } } if (!shouldInsertVersion && existingVersionRow) { - const existingVersionHash = await resolveStoredSemanticSourceHash({ - kind: "layout", - row: existingVersionRow, - fallback: () => - context.resolveStoredVersionContentHash(existingVersionRow), - }); if (existingVersionHash !== incomingHash) {Also applies to: 471-516
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/lib/storage/internal/domains/dslAssets.ts` around lines 187 - 232, In saveLayoutDSL and saveComponentDSL, cache the existingVersionHash returned by the first resolveStoredSemanticSourceHash call for each existingVersionRow and reuse it in the subsequent !shouldInsertVersion update check. Ensure the cached value is available across both branches while preserving the existing conflict and update behavior.aria/tests/actions/save-action-nonce.test.ts (1)
2-2: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
astro:actionsfor the local test import.Astro 7.1.3 exports
ActionErrorfrom the publicastro:actionsvirtual module, including its client runtime. Avoid importing fromnode_modules/astro/dist/actions/runtime/client.js, which is a published package internal build artifact.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/tests/actions/save-action-nonce.test.ts` at line 2, Update the import in save-action-nonce.test.ts to import ActionError from Astro’s public astro:actions virtual module instead of the internal node_modules runtime path, preserving the existing ClientActionError alias.aria/lib/storage/internal/domains/pageLifecycle.ts (1)
424-462: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRemove the duplicate semantic-hash computation for
existingVersionRow.
resolveStoredSemanticSourceHashruns twice with the same arguments (kind: "page",row: existingVersionRow, samefallback): once at Line 431-436 to decideshouldInsertVersion, and again at Line 457-462 to decide whether to run theUPDATE. Each call re-parsesdsl_jsonand re-runs normalization and hashing on the same row. Compute the hash once and reuse the result.♻️ Proposed fix to compute the existing-version hash once
let shouldInsertVersion = true; const existingVersionRow = await context.getStoredVersionRow( "aria_page_versions", id, version, ); + let existingVersionHash: string | undefined; if (existingVersionRow) { - const existingVersionHash = await resolveStoredSemanticSourceHash({ + existingVersionHash = await resolveStoredSemanticSourceHash({ kind: "page", row: existingVersionRow, fallback: () => context.resolveStoredVersionContentHash(existingVersionRow), }); if (existingVersionHash !== incomingHash) { if (!parsedOptions.overwriteVersionIfExists) { throw new Error( `Page version conflict for ${id}: version ${version} already exists with different content`, ); } shouldInsertVersion = false; } else { shouldInsertVersion = false; } } const versionedDSL = { ...normalizedDSL, version, updatedAt: now, }; let committedWithGuard = false; if (!shouldInsertVersion && existingVersionRow) { - const existingVersionHash = await resolveStoredSemanticSourceHash({ - kind: "page", - row: existingVersionRow, - fallback: () => - context.resolveStoredVersionContentHash(existingVersionRow), - }); - if (existingVersionHash !== incomingHash) { + if (existingVersionHash !== incomingHash) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/lib/storage/internal/domains/pageLifecycle.ts` around lines 424 - 462, Compute the existing version’s semantic hash once in the version-check flow around resolveStoredSemanticSourceHash, store it for reuse, and use that stored value in the later !shouldInsertVersion update decision. Preserve the existing conflict and overwrite behavior while eliminating the second call for the same existingVersionRow.aria/scripts/check-rendering-writer-boundaries.ts (1)
41-56: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy liftTighten the boundary check beyond whole-file matching.
inspectRenderingWriterSourcetreats a file as safe if it contains a normalizer import and call ANYWHERE in the file, regardless of whether that normalizer actually guards the matched direct write. A file that already has one legitimate normalized writer (for examplepageLifecycle.ts) would pass this check even if someone adds a second, unrelated, un-normalizeddsl_jsonwrite to a different function in the same file. This leaves a gap in exactly the files most likely to receive new direct writers.Consider narrowing the check to require the normalizer call to appear near each matched write (for example, within the same enclosing function, or within a fixed line-window before the write), rather than anywhere in the file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/scripts/check-rendering-writer-boundaries.ts` around lines 41 - 56, Update inspectRenderingWriterSource so each direct dsl_json write matched by VERSION_WRITE_RE and DSL_COLUMN_RE is validated against a nearby normalizer call, rather than accepting any NORMALIZER_IMPORT_RE and NORMALIZER_CALL_RE match anywhere in the file. Scope the association to the same enclosing function or an equivalent bounded context, while preserving the existing safe result when every matched write is guarded.aria/lib/storage/internal/domains/surfaceNormalization.ts (1)
48-66: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winLog the fallback path instead of swallowing every error silently.
The
catchblock returnsinput.fallback()for any failure, not just the "missingdsl_json" case. IfnormalizeSurfaceForPersistencethrows for an unexpected reason (a bug in canonical normalization, an unexpected DSL shape), this silently degrades every stored-hash comparison to the legacy content-hash path with no signal. Add a log call before falling back so a normalization regression surfaces during change-detection instead of being hidden.🔍 Proposed fix to surface unexpected normalization failures
try { const dslJson = input.row.dsl_json ?? input.row.dslJson; if (typeof dslJson !== "string") { return input.fallback(); } const normalized = await normalizeSurfaceForPersistence( input.kind, JSON.parse(dslJson), ); return normalized.sourceHash; - } catch { + } catch (error) { + log("warn", "Falling back to legacy content hash", { + kind: input.kind, + error: error instanceof Error ? error.message : String(error), + }); return input.fallback(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@aria/lib/storage/internal/domains/surfaceNormalization.ts` around lines 48 - 66, Update resolveStoredSemanticSourceHash so its catch block logs the normalization or parsing failure before invoking input.fallback(). Include the caught error and enough context to identify the failed stored semantic source hash resolution, while preserving the existing fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@aria/actions/library.ts`:
- Line 839: Update the install flow containing packVersion to source the
semantic version from the aria-free-pack entry in SEEDED_REGISTRY_MANIFEST when
available, rather than ARIA_FREE_BUTTON_COMPONENT.version. Preserve the existing
fallback behavior only when no matching manifest entry exists, and ensure the
value remains independent of the component storage revision.
In `@aria/actions/nodes.ts`:
- Line 455: Restore BuilderNodeSchema validation for the preflighted nodes in
the relevant action flow before normalization, using the parsed result as the
typed BuilderNode[] value instead of the unchecked cast near
normalizeNodeForPersist. Apply the same structural validation to the insertNode
path around its nodes schema, so invalid primitives or objects cannot reach
persistence.
- Around line 1092-1103: The replacement-node ID mismatch in the preflight
validation must throw RenderContractError rather than createError so safe
render-error handling can serialize it. Update the mismatch branch in the
preflightBuilderNodeInput flow to construct RenderContractError with
createRenderFailure and preserve the existing RENDER_INPUT_INVALID metadata.
In `@aria/admin/lib/actions/actionResult.ts`:
- Around line 49-54: Update the error-message selection in the visible
action-result helper so empty decoded messages and empty result.error.message
values are treated as missing, allowing fallbackMessage to be returned. Preserve
the existing precedence for non-empty decoded and transport error messages so
unwrapActionPayload continues receiving a meaningful error string.
In `@aria/lib/blocks/nodesToHtml.ts`:
- Around line 1229-1230: Update aria/lib/blocks/nodesToHtml.ts lines 1229-1230
in collectResponsiveStyleRules to resolve the tag with the same context-aware
logic as renderNode, including canonical icons emitting svg, before building
responsive selectors. Apply the identical tag resolution in
collectNodeStylesheet at lines 1317-1318 before collectNodeStylesheetRules.
In `@aria/lib/rendering/canonical/normalizeEditableSurface.ts`:
- Around line 456-483: Update the normalization flow around
restoreJsonValueFields and the options.freeze branch so restored source fields
are cloned before deepFreeze can reach them, preserving the caller-owned input
objects while retaining the normalized values and serialization behavior. Ensure
the cloned values cover every field restored by restoreJsonValueFields,
including props, a11y, frontmatter, structuredData, filter, fallback, and
overrides.
In `@aria/lib/schemas/nodes.ts`:
- Line 834: Update ComponentDSLSchema.packVersion to use the shared version
schema instead of z.string().optional(), and reuse that same schema for
PackManifestSchema.version. Preserve the repository’s x.y.z validation and
intended prerelease/build-version policy, keeping the field optional where
currently applicable.
In `@aria/tests/storage/cloudflare-storage.test.ts`:
- Around line 287-322: Update the no-rows-created assertions in
aria/tests/storage/cloudflare-storage.test.ts#L287-L322 and
aria/tests/storage/sqlite-storage.test.ts#L250-L281 to replace both hardcoded
'page-home' query values with bound samplePage.id parameters, while preserving
the existing COUNT(*) checks for aria_page_versions and aria_page_meta.
In `@package.json`:
- Line 105: Wire the package.json check:rendering-writers script into the
aggregate check commands, including check:push-guards and npm run check, and add
it to the relevant CI workflow check steps. Run it alongside the existing checks
so direct DSL writer boundary violations fail automated validation.
---
Nitpick comments:
In `@aria/actions/nodes.ts`:
- Around line 71-80: Centralize the collection-to-surface mapping by exporting a
helper next to RenderSurfaceKind in aria/lib/rendering/canonical/contract.ts,
with a CollectionType parameter. Remove the local collectionSurfaceKind
definitions and import the shared helper in aria/actions/nodes.ts (lines 71-80),
aria/admin/features/Agent/lib/tools/content/nodeWriteTools.ts (lines 45-51), and
aria/admin/features/Agent/client/composables/useAgentClientTools.ts (lines
53-59).
In `@aria/lib/migrations/propMigrations.ts`:
- Around line 108-111: Update the migration error handling in migratePageDSL to
preserve structured logging and the error stack/context previously emitted by
the logger, rather than using console.error with only error.message. If
console.error is required for edge-runtime portability, add a brief comment
documenting that constraint.
In `@aria/lib/storage/internal/domains/dslAssets.ts`:
- Around line 187-232: In saveLayoutDSL and saveComponentDSL, cache the
existingVersionHash returned by the first resolveStoredSemanticSourceHash call
for each existingVersionRow and reuse it in the subsequent !shouldInsertVersion
update check. Ensure the cached value is available across both branches while
preserving the existing conflict and update behavior.
In `@aria/lib/storage/internal/domains/pageLifecycle.ts`:
- Around line 424-462: Compute the existing version’s semantic hash once in the
version-check flow around resolveStoredSemanticSourceHash, store it for reuse,
and use that stored value in the later !shouldInsertVersion update decision.
Preserve the existing conflict and overwrite behavior while eliminating the
second call for the same existingVersionRow.
In `@aria/lib/storage/internal/domains/surfaceNormalization.ts`:
- Around line 48-66: Update resolveStoredSemanticSourceHash so its catch block
logs the normalization or parsing failure before invoking input.fallback().
Include the caught error and enough context to identify the failed stored
semantic source hash resolution, while preserving the existing fallback
behavior.
In `@aria/scripts/check-rendering-writer-boundaries.ts`:
- Around line 41-56: Update inspectRenderingWriterSource so each direct dsl_json
write matched by VERSION_WRITE_RE and DSL_COLUMN_RE is validated against a
nearby normalizer call, rather than accepting any NORMALIZER_IMPORT_RE and
NORMALIZER_CALL_RE match anywhere in the file. Scope the association to the same
enclosing function or an equivalent bounded context, while preserving the
existing safe result when every matched write is guarded.
In `@aria/tests/actions/save-action-nonce.test.ts`:
- Line 2: Update the import in save-action-nonce.test.ts to import ActionError
from Astro’s public astro:actions virtual module instead of the internal
node_modules runtime path, preserving the existing ClientActionError alias.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0d68f518-f3c1-42de-94aa-747ff711ac41
📒 Files selected for processing (49)
aria/actions/_renderContractError.tsaria/actions/crud.tsaria/actions/library.tsaria/actions/nodes.tsaria/actions/save.tsaria/admin/composables/useActions.tsaria/admin/features/Agent/client/composables/useAgentClientTools.tsaria/admin/features/Agent/lib/schemas/toolProtocol.tsaria/admin/features/Agent/lib/tools/content/nodeWriteTools.tsaria/admin/features/Agent/lib/tools/toolErrors.tsaria/admin/features/Core/composables/useSavePublish.tsaria/admin/features/Studio/composer/composables/libraryActionResults.tsaria/admin/lib/actions/actionResult.tsaria/lib/blocks/nodesToAstro.tsaria/lib/blocks/nodesToHtml.tsaria/lib/migrations/propMigrations.tsaria/lib/rendering/actionErrorMessage.tsaria/lib/rendering/canonical/contract.tsaria/lib/rendering/canonical/index.tsaria/lib/rendering/canonical/legacyNodeCompatibility.tsaria/lib/rendering/canonical/normalizeEditableSurface.tsaria/lib/rendering/canonical/preflight.tsaria/lib/rendering/canonical/stableJson.tsaria/lib/schemas/nodes.tsaria/lib/storage/internal/domains/dslAssets.tsaria/lib/storage/internal/domains/pageLifecycle.tsaria/lib/storage/internal/domains/surfaceNormalization.tsaria/lib/storage/sqlitePlatform.tsaria/lib/types/nodes.tsaria/scripts/bootstrap-remote-storage.tsaria/scripts/check-rendering-writer-boundaries.tsaria/scripts/cleanup-navigation-bindings.tsaria/scripts/migrate-layout-slot-defaults.tsaria/scripts/seed-d1-storage.tsaria/scripts/seed-header-01.tsaria/tests/actions/library-pack-version.test.tsaria/tests/actions/save-action-nonce.test.tsaria/tests/composables/actionResult.test.tsaria/tests/composables/libraryActionResults.test.tsaria/tests/features/agent/tools/tool-errors.test.tsaria/tests/icons/action-handlers.integration.test.tsaria/tests/lib/nodesToHtml.test.tsaria/tests/rendering-foundation/normalization.parity.test.tsaria/tests/scripts/rendering-writer-boundaries.test.tsaria/tests/storage/cloudflare-storage.test.tsaria/tests/storage/sqlite-storage.test.tspackage.jsonvitest.rendering-foundation.node.config.tsvitest.rendering-foundation.workerd.config.ts
💤 Files with no reviewable changes (1)
- aria/tests/lib/nodesToHtml.test.ts
| ...ARIA_FREE_BUTTON_COMPONENT, | ||
| source: "aria" as const, | ||
| packId: "aria-free-pack", | ||
| packVersion: ARIA_FREE_BUTTON_COMPONENT.version ?? "1.0.0", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
packVersion is sourced from the component storage revision.
ARIA_FREE_BUTTON_COMPONENT.version is the storage revision field. ComponentDSL documents packVersion as the "Installed pack semantic version; independent of storage revision". Every other install path sets packVersion from pack.version in the manifest.
If version is a revision counter such as "3", parseVersion returns null, resolveInstalledPackVersion returns UNKNOWN_PACK_VERSION, and checkUpdates at line 948 then reports a permanent update for the starter pack. Use a semantic pack version instead.
🐛 Proposed fix
- packVersion: ARIA_FREE_BUTTON_COMPONENT.version ?? "1.0.0",
+ packVersion: "1.0.0",Prefer the manifest entry for aria-free-pack if one exists in SEEDED_REGISTRY_MANIFEST.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| packVersion: ARIA_FREE_BUTTON_COMPONENT.version ?? "1.0.0", | |
| packVersion: "1.0.0", |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/actions/library.ts` at line 839, Update the install flow containing
packVersion to source the semantic version from the aria-free-pack entry in
SEEDED_REGISTRY_MANIFEST when available, rather than
ARIA_FREE_BUTTON_COMPONENT.version. Preserve the existing fallback behavior only
when no matching manifest entry exists, and ensure the value remains independent
of the component storage revision.
| id: IdSchema, | ||
| parentId: z.string().nullable(), | ||
| nodes: z.array(BuilderNodeSchema).min(1), | ||
| nodes: z.array(z.unknown()).min(1), |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Restore structural node validation after preflight.
nodes accepted BuilderNodeSchema before this change. It now accepts z.array(z.unknown()). Line 924 makes the same change for insertNode.
preflightBuilderNodeArrayInput does not replace that validation. It validates the JSON graph only: prototypes, accessors, cycles, size, depth, and value types. It accepts a plain string, a number, or an object with no id, type, props, styles, or children. The result is then cast at line 656 with as BuilderNode[], which the compiler cannot check.
A request with nodes: ["x"] therefore reaches normalizeNodeForPersist at line 658 with a primitive, and nodeIds at line 698 resolves to undefined. Non-node data can be persisted into doc.nodes.
Parse the preflighted source with BuilderNodeSchema before normalization, and remove the unchecked cast.
🛡️ Proposed fix for the insert paths
- const preflightedNodes = preflightBuilderNodeArrayInput({
- kind: collectionSurfaceKind(input.collection),
- nodes: input.nodes,
- }).source as BuilderNode[];
- const normalizedNodes = preflightedNodes.map((node) =>
+ const preflightedNodes = z
+ .array(BuilderNodeSchema)
+ .parse(
+ preflightBuilderNodeArrayInput({
+ kind: collectionSurfaceKind(input.collection),
+ nodes: input.nodes,
+ }).source,
+ );
+ const normalizedNodes = preflightedNodes.map((node) =>
normalizeNodeForPersist({
...node,
id: node.id || generateNodeId(),
} as BuilderNode),
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| nodes: z.array(z.unknown()).min(1), | |
| const preflightedNodes = z | |
| .array(BuilderNodeSchema) | |
| .parse( | |
| preflightBuilderNodeArrayInput({ | |
| kind: collectionSurfaceKind(input.collection), | |
| nodes: input.nodes, | |
| }).source, | |
| ); | |
| const normalizedNodes = preflightedNodes.map((node) => | |
| normalizeNodeForPersist({ | |
| ...node, | |
| id: node.id || generateNodeId(), | |
| } as BuilderNode), | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/actions/nodes.ts` at line 455, Restore BuilderNodeSchema validation for
the preflighted nodes in the relevant action flow before normalization, using
the parsed result as the typed BuilderNode[] value instead of the unchecked cast
near normalizeNodeForPersist. Apply the same structural validation to the
insertNode path around its nodes schema, so invalid primitives or objects cannot
reach persistence.
| if (preflightedReplacement.id !== input.nodeId) { | ||
| throw createError( | ||
| "RENDER_INPUT_INVALID", | ||
| "The render input is invalid.", | ||
| { | ||
| surfaceKind: collectionSurfaceKind(input.collection), | ||
| stage: "preflight", | ||
| issue: "replacement-node-id-mismatch", | ||
| issueCount: 1, | ||
| }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check what toSafeRenderContractActionError matches and the encoded message constant.
set -euo pipefail
fd -t f '_renderContractError.ts' aria/actions --exec cat -n {}
fd -t f 'actionErrorMessage.ts' aria/lib/rendering --exec cat -n {}
rg -nP --type=ts -C3 'export function createError' aria/lib aria/actionsRepository: ariabuilder/aria
Length of output: 2981
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## nodes.ts replaceNode section"
sed -n '1040,1125p' aria/actions/nodes.ts | cat -n | sed 's/^/1040-1125: /'
echo
echo "## _shared createError implementation"
sed -n '280,360p' aria/actions/_shared.ts | cat -n | sed 's/^/280-360: /'
echo
echo "## canonical render contract errors"
fd -t f 'canonical.ts' aria/lib/rendering --exec rg -n -C4 'class RenderContractError|RenderContractError|createRenderFailure|RENDER_INPUT_INVALID' {}
echo
echo "## preflight RENDER_INPUT_INVALID usage"
fd -t f 'preflight.ts' aria/lib/rendering --exec ast-grep outline {} --view compact | sed -n '1,200p'
fd -t f 'preflight.ts' aria/lib/rendering --exec rg -n -C4 'createRenderFailure|RENDER_INPUT_INVALID|render input' {}Repository: ariabuilder/aria
Length of output: 8100
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## createError implementation"
sed -n '320,355p' aria/actions/_shared.ts | cat -n | sed 's/^/320-355: /'
echo
echo "## canonical render contract errors"
fd -t f 'canonical.ts' aria/lib/rendering --exec sh -c 'echo "-- {}"; sed -n "1,220p" "{}" | cat -n'
echo
echo "## render contract error behavior probe from source"
python3 - <<'PY'
from pathlib import Path
p = Path('aria/actions/_shared.ts')
s = p.read_text()
start = s.find('export function createError(')
end = s.find('\nexport function isActionError(', start)
print(s[start:end])
PYRepository: ariabuilder/aria
Length of output: 2048
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## aria/actions/nodes.ts imports and relevant constants"
sed -n '1,80p' aria/actions/nodes.ts | cat -n | sed 's/^/1-80: /'
echo
echo "## handleError continuation"
sed -n '1135,1175p' aria/actions/nodes.ts | cat -n | sed 's/^/1135-1175: /'
echo
echo "## RenderContractError definitions and createRenderFailure usages"
rg -n -C5 'class RenderContractError|export .*RenderContractError|createRenderFailure|RENDER_INPUT_INVALID' aria/lib/rendering aria/actions
echo
echo "## precise code probe: toSafeRenderContractActionError and createError/decode message shapes"
sed -n '13,23p' aria/actions/_renderContractError.ts | cat -n | sed 's/^/13-23: /'
sed -n '327,343p' aria/actions/_shared.ts | cat -n | sed 's/^/327-343: /'
sed -n '15,24p' aria/lib/rendering/actionErrorMessage.ts | cat -n | sed 's/^/15-24: /'
python3 - <<'PY'
from pathlib import Path
src = Path('aria/actions/_shared.ts').read_text()
start = src.find('export function createError(')
end = src.find('\nexport function isActionError(', start+1)
code = src[start:end]
print(code)
print("returns structured object:", "ActionError = {" in code or "return {" in code)
PYRepository: ariabuilder/aria
Length of output: 25179
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## handleError full"
sed -n '360,415p' aria/actions/_shared.ts | cat -n | sed 's/^/360-415: /'Repository: ariabuilder/aria
Length of output: 2570
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## imports around createError"
rg -n 'CREATE.*ERROR|ERROR_CODES|from.*_shared|createError\(' aria/actions/nodes.ts | sed -n '1,60p'
echo
echo "## create error enum definitions"
rg -n -C3 'const ERROR_CODES|export const ERROR_CODES|INVALID_INPUT|NODE_NOT_FOUND|RENDER_INPUT_INVALID' aria/actions/_shared.ts \
aria/actions/nodes.ts \
aria/actions/library.ts \
aria/lib/rendering/canonical \
aria/lib/rendering/actionErrorMessage.tsRepository: ariabuilder/aria
Length of output: 15962
Throw RenderContractError for the ID mismatch instead of createError.
toSafeRenderContractActionError only accepts RenderContractError; this code returns a structured action error, so it is not serialized by safe render-error handling. Use the existing preflightBuilderNodeInput rejection pattern, e.g. new RenderContractError(createRenderFailure("RENDER_INPUT_INVALID", { surfaceKind, stage: "preflight", issue: "replacement-node-id-mismatch", issueCount: 1 })).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/actions/nodes.ts` around lines 1092 - 1103, The replacement-node ID
mismatch in the preflight validation must throw RenderContractError rather than
createError so safe render-error handling can serialize it. Update the mismatch
branch in the preflightBuilderNodeInput flow to construct RenderContractError
with createRenderFailure and preserve the existing RENDER_INPUT_INVALID
metadata.
| return ( | ||
| decodeRenderActionErrorMessage(result.error.message)?.message ?? | ||
| result.error.message ?? | ||
| fallbackMessage | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
An empty error.message bypasses the fallback and suppresses the error.
?? only replaces null and undefined. If the transport returns error: { message: "" }, this function returns "". unwrapActionPayload at line 93 then treats the result as non-errored and continues to parse result.data. Use a non-empty check.
🐛 Proposed fix
- return (
- decodeRenderActionErrorMessage(result.error.message)?.message ??
- result.error.message ??
- fallbackMessage
- );
+ const decoded = decodeRenderActionErrorMessage(result.error.message);
+ if (decoded) return decoded.message;
+ const message = result.error.message;
+ return typeof message === "string" && message.trim().length > 0
+ ? message
+ : fallbackMessage;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/admin/lib/actions/actionResult.ts` around lines 49 - 54, Update the
error-message selection in the visible action-result helper so empty decoded
messages and empty result.error.message values are treated as missing, allowing
fallbackMessage to be returned. Preserve the existing precedence for non-empty
decoded and transport error messages so unwrapActionPayload continues receiving
a meaningful error string.
| if (Object.keys(renderStyles).length > 0) { | ||
| const tag = getRenderedStyleTag(node, renderProps); | ||
| const tag = getNativeTagForRenderableNode(node, renderProps) ?? "div"; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
CSS selectors can target a different tag than the renderer emits in aria/lib/blocks/nodesToHtml.ts. The removal of getRenderedStyleTag is the shared root cause. Both style collectors now resolve the tag with getNativeTagForRenderableNode(node, renderProps) ?? "div", while renderNode uses getNativeTagForRenderableNodeInContext and emits an inline <svg> for canonical icons. getResponsiveStyleSelector builds the selector from that tag, so the generated rules can fail to match the emitted element.
aria/lib/blocks/nodesToHtml.ts#L1229-L1230: resolve the tag incollectResponsiveStyleRuleswith the same context-aware logic asrenderNode, including the canonical-iconsvgcase.aria/lib/blocks/nodesToHtml.ts#L1317-L1318: apply the same tag resolution incollectNodeStylesheetbefore callingcollectNodeStylesheetRules.
📍 Affects 1 file
aria/lib/blocks/nodesToHtml.ts#L1229-L1230(this comment)aria/lib/blocks/nodesToHtml.ts#L1317-L1318
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/lib/blocks/nodesToHtml.ts` around lines 1229 - 1230, Update
aria/lib/blocks/nodesToHtml.ts lines 1229-1230 in collectResponsiveStyleRules to
resolve the tag with the same context-aware logic as renderNode, including
canonical icons emitting svg, before building responsive selectors. Apply the
identical tag resolution in collectNodeStylesheet at lines 1317-1318 before
collectNodeStylesheetRules.
| const parsed = applyAuthoritativeSchema(input.kind, source); | ||
| restoreJsonValueFields(source, parsed as unknown as Record<string, unknown>); | ||
| assertPortableObjectPrototypes(input.kind, parsed); | ||
| removeUndefinedObjectProperties(parsed); | ||
| const serialized = stableSerializeJson(parsed); | ||
| const canonicalSourceBytes = new TextEncoder().encode(serialized).byteLength; | ||
| if (canonicalSourceBytes > MAX_CANONICAL_SOURCE_BYTES) { | ||
| throw new RenderContractError( | ||
| createRenderFailure("RENDER_INPUT_INVALID", { | ||
| surfaceKind: input.kind, | ||
| stage: "canonical-size", | ||
| issue: "source-size", | ||
| violatedLimit: "canonicalSourceBytes", | ||
| maximum: MAX_CANONICAL_SOURCE_BYTES, | ||
| actual: canonicalSourceBytes, | ||
| issueCount: 1, | ||
| }), | ||
| ); | ||
| } | ||
| const normalized: NormalizedRenderSurface<K> = { | ||
| contractVersion: 1, | ||
| kind: input.kind, | ||
| source: parsed, | ||
| sourceHash: await sha256Text(serialized), | ||
| }; | ||
|
|
||
| return options.freeze === true ? deepFreeze(normalized) : normalized; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check whether preflightEditableSurface deep-clones the input source.
set -euo pipefail
rg -n -C 5 'export function preflightEditableSurface' aria/lib/rendering/canonical/preflight.ts
rg -n -C 3 'structuredClone|JSON\.parse\(JSON\.stringify' aria/lib/rendering/canonical/preflight.ts
rg -n -C 3 'freeze: *true' aria/lib aria/actions aria/scriptsRepository: ariabuilder/aria
Length of output: 448
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- relevant normalizeEditableSurface section ---\n'
sed -n '220,490p' aria/lib/rendering/canonical/normalizeEditableSurface.ts | cat -n
printf '\n--- preflightEditableSurface and graph prefabs ---\n'
sed -n '330,390p' aria/lib/rendering/canonical/preflight.ts | cat -n
rg -n -C 4 'function preflightJsonGraph|structuredClone|JSON\.parse\(JSON\.stringify|deepFreeze|freeze' aria/lib/rendering/canonical/preflight.ts aria/lib/rendering/canonical/normalizeEditableSurface.ts
printf '\n--- normalizeSurfaceNodeTrees references ---\n'
rg -n -C 5 'normalizeSurfaceNodeTrees|preflightEditableSurface|options\.freeze' aria/lib/rendering aria/actions aria/scripts || trueRepository: ariabuilder/aria
Length of output: 245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- import/export outline for canonical files ---\n'
ast-grep outline aria/lib/rendering/canonical/preflight.ts --view compact || true
ast-grep outline aria/lib/rendering/canonical/normalizeEditableSurface.ts --view compact || true
printf '\n--- deepFreeze implementation usages ---\n'
fd -a . aria/lib | rg 'deepFreeze|freeze' || true
rg -n -C 4 'function deepFreeze|const deepFreeze|export .*deepFreeze' aria/lib aria/actions aria/scripts || trueRepository: ariabuilder/aria
Length of output: 245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- relevant normalizeEditableSurface section ---'
sed -n '220,490p' aria/lib/rendering/canonical/normalizeEditableSurface.ts
echo
echo '--- preflightEditableSurface and graph prefabs ---'
sed -n '330,390p' aria/lib/rendering/canonical/preflight.ts
rg -n -C 4 'function preflightJsonGraph|structuredClone|JSON\.parse|(?:JSON\.stringify)|deepFreeze|freeze' aria/lib/rendering/canonical/preflight.ts aria/lib/rendering/canonical/normalizeEditableSurface.ts
echo
echo '--- normalizeSurfaceNodeTrees references ---'
rg -n -C 5 'normalizeSurfaceNodeTrees|preflightEditableSurface|options\.freeze' aria/lib/rendering aria/actions aria/scripts || true
echo
echo '--- deepFreeze implementation/usages ---'
rg -n -C 4 'function deepFreeze|const deepFreeze|export .*deepFreeze|deepFreeze\(' aria/lib aria/actions aria/scripts || trueRepository: ariabuilder/aria
Length of output: 20260
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- preflightJsonGraph body ---'
sed -n '112,220p' aria/lib/rendering/canonical/preflight.ts
echo
echo '--- normalizeSurfaceNodeTrees / normalizeNodeTree bodies ---'
sed -n '150,195p' aria/lib/rendering/canonical/normalizeEditableSurface.ts
echo
echo '--- shallow-copy/freezing behavior probe ---'
node - <<'JS'
function normalizeSurfaceNodeTrees(source) {
const normalized = { ...source };
return normalized;
}
function applyAuthoritativeSchema(source) {
return JSON.parse(JSON.stringify(source));
}
function restoreJsonValueFields(source, parsed) {
parsed.nested = source.nested;
return { serialized: JSON.stringify(parsed) };
}
function deepFreeze(value) {
const stack = [];
if (value && typeof value === "object") stack.push(value);
while (stack.length > 0) {
const current = stack.pop();
if (!current || Object.isFrozen(current)) continue;
for (const child of Object.values(current)) {
if (child && typeof child === "object" && !Object.isFrozen(child)) stack.push(child);
}
Object.freeze(current);
}
return value;
}
const input = { source: { root: "x", nested: { value: 1 } } };
const preflight = { source: input.source };
let source = normalizeSurfaceNodeTrees(preflight.source);
const parsed = applyAuthoritativeSchema(source);
const serialized = restoreJsonValueFields(source, parsed).serialized;
const normalized = { kind: "page", source: parsed };
deepFreeze(normalized);
source.root = "z";
try {
source.nested.value = 2;
console.log("sloppy write succeeded, nested.value =", source.nested.value, "parsed.nested.value =", parsed.nested.value);
} catch (e) {
console.log("sloppy write threw:", e.name + ": " + e.message);
}
try {
"use strict";
parsed.nested.value = 3;
console.log("strict write succeeded, parsed.nested.value =", parsed.nested.value);
} catch (e) {
console.log("strict write threw:", e.name + ": " + e.message);
}
console.log("normalized source frozen:", Object.isFrozen(normalized.source), "parsed.nested frozen:", Object.isFrozen(normalized.source.nested));
JSRepository: ariabuilder/aria
Length of output: 5252
Preserve the caller’s source when freezing canonical normalized surfaces.
restoreJsonValueFields copies props, a11y, frontmatter, structuredData, filter, fallback, and overrides by reference into parsed. When options.freeze === true, deepFreeze(normalized) freezes those caller-owned objects. A later write can fail silently in sloppy mode or throw in strict mode, so clone the restored values before freezing or document that the caller must not mutate the original surface after normalization is called with freeze: true.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/lib/rendering/canonical/normalizeEditableSurface.ts` around lines 456 -
483, Update the normalization flow around restoreJsonValueFields and the
options.freeze branch so restored source fields are cloned before deepFreeze can
reach them, preserving the caller-owned input objects while retaining the
normalized values and serialization behavior. Ensure the cloned values cover
every field restored by restoreJsonValueFields, including props, a11y,
frontmatter, structuredData, filter, fallback, and overrides.
|
|
||
| source: ComponentSourceSchema.optional(), // Defaults to 'custom' for existing components | ||
| packId: z.string().optional(), // Pack identifier for aria-sourced components | ||
| packVersion: z.string().optional(), // Installed pack version, not storage revision |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Validate packVersion with the pack version schema.
Line [834] accepts any string, including "" and "latest". PackManifestSchema.version at Lines [898]-[900] enforces the repository’s x.y.z version contract. Since ComponentDSLSchema is used by authoritative normalization, invalid pack metadata can be persisted.
Reuse one shared version schema for packVersion and PackManifestSchema.version, including the intended prerelease/build-version policy.
Proposed fix
- packVersion: z.string().optional(), // Installed pack version, not storage revision
+ packVersion: PackVersionSchema.optional(), // Installed pack version, not storage revision🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/lib/schemas/nodes.ts` at line 834, Update ComponentDSLSchema.packVersion
to use the shared version schema instead of z.string().optional(), and reuse
that same schema for PackManifestSchema.version. Preserve the repository’s x.y.z
validation and intended prerelease/build-version policy, keeping the field
optional where currently applicable.
| it("rejects invalid recursive page input before creating D1 rows", async () => { | ||
| const adapter = new CloudflareStorageAdapter({ | ||
| aria_db: createD1Mock(client) as any, | ||
| }); | ||
| const cyclicNode: Record<string, unknown> = { | ||
| id: "cycle", | ||
| type: "Container", | ||
| props: {}, | ||
| styles: {}, | ||
| }; | ||
| cyclicNode.children = [cyclicNode]; | ||
|
|
||
| await expect( | ||
| adapter.savePageDSL(samplePage.id, { | ||
| ...samplePage, | ||
| nodes: [cyclicNode], | ||
| } as unknown as PageDSL), | ||
| ).rejects.toMatchObject({ | ||
| failure: { | ||
| code: "RENDER_INPUT_INVALID", | ||
| context: expect.objectContaining({ stage: "preflight" }), | ||
| }, | ||
| }); | ||
|
|
||
| const [versions, metadata] = await Promise.all([ | ||
| client.execute( | ||
| "SELECT COUNT(*) AS count FROM aria_page_versions WHERE id = 'page-home'", | ||
| ), | ||
| client.execute( | ||
| "SELECT COUNT(*) AS count FROM aria_page_meta WHERE id = 'page-home'", | ||
| ), | ||
| ]); | ||
| expect(Number(versions.rows[0]?.count)).toBe(0); | ||
| expect(Number(metadata.rows[0]?.count)).toBe(0); | ||
| }); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate test files =="
git ls-files | rg 'api/tests/storage/(cloudflare|sqlite)-storage\.test\.ts$' || true
echo
echo "== Cloudflare test relevant section =="
if [ -f aria/tests/storage/cloudflare-storage.test.ts ]; then
wc -l aria/tests/storage/cloudflare-storage.test.ts
sed -n '1,80p;260,330p' aria/tests/storage/cloudflare-storage.test.ts | cat -n
fi
echo
echo "== SQLite test relevant section =="
if [ -f aria/tests/storage/sqlite-storage.test.ts ]; then
wc -l aria/tests/storage/sqlite-storage.test.ts
sed -n '1,80p;230,290p' aria/tests/storage/sqlite-storage.test.ts | cat -n
fi
echo
echo "== Search for samplePage id definition/usages =="
rg -n "samplePage|page-home|savePageDSL|COUNT\\(\\*\\)" aria/tests/storage -SRepository: ariabuilder/aria
Length of output: 46516
Query by samplePage.id instead of the hardcoded 'page-home' literal in the no-rows-created assertions.
Both tests call adapter.savePageDSL(samplePage.id, ...) but check aria_page_versions and aria_page_meta with the literal 'page-home'. Use samplePage.id as a parameter so later changes to the test fixture do not make this assertion check an unrelated page id.
aria/tests/storage/cloudflare-storage.test.ts#L287-L322: replace the two'page-home'literals in theCOUNT(*)queries with a boundsamplePage.id.aria/tests/storage/sqlite-storage.test.ts#L250-L281: replace the two'page-home'literals in theCOUNT(*)queries with a boundsamplePage.id.
📍 Affects 2 files
aria/tests/storage/cloudflare-storage.test.ts#L287-L322(this comment)aria/tests/storage/sqlite-storage.test.ts#L250-L281
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@aria/tests/storage/cloudflare-storage.test.ts` around lines 287 - 322, Update
the no-rows-created assertions in
aria/tests/storage/cloudflare-storage.test.ts#L287-L322 and
aria/tests/storage/sqlite-storage.test.ts#L250-L281 to replace both hardcoded
'page-home' query values with bound samplePage.id parameters, while preserving
the existing COUNT(*) checks for aria_page_versions and aria_page_meta.
| "test:rendering:canvas-parity": "playwright test --config playwright.rendering-parity.config.ts", | ||
| "test:runtime-stability:workerd": "vitest run --config vitest.runtime-stability.workerd.config.ts", | ||
| "check:rendering-boundaries": "node --import tsx aria/scripts/check-rendering-foundation-boundaries.ts", | ||
| "check:rendering-writers": "node --import tsx aria/scripts/check-rendering-writer-boundaries.ts", |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm check:rendering-writers is wired into an aggregate script or CI workflow.
rg -n 'check:rendering-writers' package.json
rg -n --type=yaml 'check:rendering-writers|npm run check|pnpm (run )?check' .github/workflows 2>/dev/nullRepository: ariabuilder/aria
Length of output: 522
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- package.json scripts context ---'
sed -n '90,115p' package.json
echo '--- CI workflow relevant context ---'
sed -n '30,55p' .github/workflows/ci.yml
echo '--- publish workflow relevant context ---'
sed -n '35,50p' .github/workflows/publish-npm.yml
echo '--- check scripts references in package scripts ---'
python3 - <<'PY'
import json
with open('package.json') as f:
data=json.load(f)
for name,cmd in data.get('scripts',{}).items():
if 'check' in name or 'check:' in str(cmd):
print(f'{name}: {cmd}')
PYRepository: ariabuilder/aria
Length of output: 4275
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- locate project-command.ts ---'
fd -a 'project-command\.ts$' .
echo '--- project command definitions ---'
PROJECT_CMD=$(fd 'project-command\.ts$' . | head -n 1)
if [ -n "${PROJECT_CMD:-}" ]; then
wc -l "$PROJECT_CMD"
rg -n "check|rendering-writers|rendering.*writer|command|check:" "$PROJECT_CMD" -C 3
fi
echo '--- all references to script name/project command check in repo ---'
rg -n 'check:rendering-writers|project-command\.ts check|rendering.*writers|dsl_json|writer.*normaliz|normaliz.*writer' .Repository: ariabuilder/aria
Length of output: 16294
Wire check:rendering-writers into aggregate checks.
check:rendering-writers is not called from check:push-guards, npm run check, or the CI workflows. Call it before/after the existing checks so un-normalized direct DSL writers fail CI instead of remaining manual-only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 105, Wire the package.json check:rendering-writers
script into the aggregate check commands, including check:push-guards and npm
run check, and add it to the relevant CI workflow check steps. Run it alongside
the existing checks so direct DSL writer boundary violations fail automated
validation.
Summary
Validation
No release, deployment, database migration execution, or bulk row rewrite is included.
Summary by CodeRabbit