Repository navigation
chore(schema): re-vendor manifest schema for cross-app storage imports (+ fork-manifest test) - #868
Merged
Merged
Conversation
Adds the fork seed manifest (minus the server-owned iframe.src) as a fixture and asserts: it validates clean; imports[0].access "write" and an import missing "from" are each refused with their exact message and field; a coherent manifest declaring apps:storage:import validates. Expected RED on this commit: the embedded schema predates cross-app storage imports (civitai web v5.1.425). The re-vendor follows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Regenerated with scripts/revendor-canonical-schema.sh from the live canonical schema (civitai web v5.1.425). Adds storage.imports (from / collections / access read|readwrite), collection-level `exportable`, the apps:storage:import scope, and relaxes storage's required `collections` to anyOf collections|imports. The new imports[].from pattern has no gloss in patternRules yet, so it is recorded as owed in internal/validate/pattern_gloss_owed.json, the same row the revendor workflow's -update-gloss-ledger step writes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…all advisory Mirrors civitai appStorageScopeErrors / parseImports for imports: - an imports-only manifest (no own collections, both scopes) validates; - imports without apps:storage, imports without apps:storage:import, and apps:storage:import with no imports are each refused with the server's text; - an import from the app itself and a second import from one source are refused; - apps:storage:import is a sensitive scope (needs a justification); - a source call with a literal `from` is checked against that import, not against storage.collections. Corpus fixtures and field/pair ledger rows for the new findings. Expected RED on this commit; the fix follows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- storageScopeRuleChecks: the three import rules of appStorageScopeErrors (imports-only without apps:storage; imports without apps:storage:import; apps:storage:import with no imports), and "apps:storage with nothing to reach" now also requires no imports, so an imports-only app validates. - storageImportChecks: parseImports' two refusals the schema admits, an import from the app itself and a second import from one source (compared, like the server, against the imports it kept). - SENSITIVE_BLOCK_SCOPES gains apps:storage:import (server set, main and release). - storageScopeChecks: a collection call with a literal `from` option is checked against the import from that source, not storage.collections. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
lint (unused): every caller reads literalArgEnd now. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ZacxDev
marked this pull request as ready for review
November 3, 2026 16:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Re-vendors
schema/app-block.manifest.schema.jsonfrom the live canonical schema (https://civitai.com/schemas/app-block/v1.json, civitai web v5.1.425) using the repo's own mechanism,scripts/revendor-canonical-schema.sh. It also adds a test that uses a real fork manifest as its fixture.Why
civitai web v5.1.425 added cross-app storage imports. The embedded schema predates them, so
civitai app validateon a real fork manifest fails with two findings the server does not raise:The only way to submit a fork today is
--skip-validate.Schema changes (canonical vs. current
main)storage.imports: a new array, 1 to 8 items. Each item is{from, collections, access}, all three required.fromis a blockId:^[a-z][a-z0-9-]{1,38}[a-z0-9]$, length 3 to 40.collectionsis 1 to 16 unique collection names.accessis an enum:read|readwrite.storage.collections.<name>.exportable: a new boolean.apps:storage:import.storagepreviously requiredcollections. It now usesanyOf: [required collections, required imports].storagedescription and thescopeJustificationslist of sensitive scopes now nameapps:storage:import.Drift vs. the bot PR #867
#867 (head
90fbe193) is byte-identical to currentmain(cmpexit 0 on the schema file). Its 56 added lines were measured against its older basea9ba713, and #865 has since brought that content ontomain. So #867 has nothing left to land, and every change listed above is beyond #867. This PR supersedes it.Pattern-gloss ledger
The new
imports[].frompattern has no gloss inpatternRules. It is therefore recorded as owed ininternal/validate/pattern_gloss_owed.json. This is the same sorted row that the revendor workflow's-update-gloss-ledgerstep writes. The file was written to that format by hand, because tests are not run locally here, andTestPatternRulesCoverTheVendoredSchemapassing in CI is the check on it.Test
internal/validate/storage_imports_test.gouses the fixtureinternal/validate/testdata/fork-manifest.json. That fixture is the real fork seed manifest minusiframe.src.iframe.srcis server-owned and the CLI refusing it is intended; the seed carrying it is a separate platform fix. The test asserts:imports[0].access = "write"gives exactly one error, at fieldstorage.imports[0].access, with messagestorage.imports[0].access: value must be one of 'read', 'readwrite';fromgives exactly one error, atstorage.imports[0], with messagestorage.imports[0]: missing property 'from';apps:storage+apps:storage:import(with a justification), one collection withexportable: false, and one import.Every expected message is a literal string. None is read from the schema.
Red/green matrix (CI)
eaaa477main's schemabuild-testfails on exactly the 4 new tests:TestForkManifestWithStorageImportsValidates,TestStorageImportRejectsUnknownAccess,TestStorageImportRejectsMissingFrom,TestStorageImportScopeValidates. The failures are the reportedscopes[N]enum error andadditional properties 'imports'(plus'exportable'for (c)). Every other job is green.ff6993aschema-driftis green: https://github.com/civitai/cli/actions/runs/41675338664On attempt 1 of the green run,
template-page-moneyfailed:src/e2e.test.tsx"estimate -> consent -> submit -> poll -> succeeded" timed out at 6115 ms against a 5000 ms limit. That run was slow across the board (tests 22.8s vs 10.1s, setup 1.72s vs 0.71s compared with the red run). That job tests the JS scaffold template and does not touch the schema. Re-running the failed job passed (attempt 2).Server-rule parity for imports (added after review)
The server's rules come from civitai
mainsrc/shared/constants/block-storage.constants.ts(appStorageScopeErrorsandparseImports) andpackages/civitai-shared/src/sensitive-block-scopes.ts. Messages are the server's text.storagemanifest.gostorageScopeRuleChecks: the existing "nothing to reach" rule fired onlen(names)==0 && hasBase. It now also requires no imports, matching the server'snames.length === 0 && sources.length === 0. An imports-only app (no collections of its own,apps:storage+apps:storage:import) now validates. Three import rules are added, in the server's order:storage imports from <src> but scopes does not include apps:storage — add it, or remove storage(imports only, no collections);storage imports from <src>, which requires the apps:storage:import scope — add it to scopes with a scopeJustifications entry;scopes includes apps:storage:import but storage declares no imports — declare them under storage.imports, or remove the scope.storagemanifest.gostorageImportCheckscovers the only twoparseImportsrefusals the schema cannot express:storage.imports[i].from names this app itself — declare its collections instead;storage.imports[i] imports from "<src>" again — declare one import per app. Like the server, this compares only against imports it KEPT, so an entry the schema already refuses never makes a later one a "duplicate".parseImportsrefusal (array bounds, keys,frompattern,accessenum, collection names/bounds/uniqueness) is already in the schema.semantic.go:apps:storage:importis added toSENSITIVE_BLOCK_SCOPES. It is in the server set on bothmainandrelease, which now holds 11 scopes.storagescope.gosource advisory, rule 1:fromoption, e.g.app.storage.collection('kv', { from: 'parent' }), is now checked against the import from that source:storage.imports;storage.imports[i].collections.import_not_declared.fromkeeps today's own-collection check.from({ from: PARENT },{ from }) cannot be resolved statically, so it is not flagged.collection('kv', opts)) is still read as an own collection. This is a documented residual.Red/green matrix for the parity change (CI)
a1ab6d0build-testfails onTestStorageImportsOnlyValidates(with the OLD guard's own message:scopes includes apps:storage but storage declares no collections),TestStorageImportScopeRules(3 subtests),TestStorageImportParseRefusals(self-import, duplicate),TestStorageImportScopeIsSensitive,TestStorageImportedCollectionAdvisory(6 subtests),TestIsSensitiveBlockScope,TestEveryCheckEmitsAField,TestEveryFindingCarriesItsDocumentedField,TestPairRuleNamesTheMissingField. All other jobs are green.51dc961build-testgreen,lintred:literalArgwas unused. https://github.com/civitai/cli/actions/runs/41678846649c8724b0The accept twins pass on both sides by design, so they are invariant guards, not regression proof: collections plus imports with both scopes, no duplicate of a refused entry, and the silent
fromcases.Until a CLI release and the seed fix
Users validating or submitting a fork manifest with a released CLI still need
--skip-validate. A CLI release with this PR is not enough on its own for a real fork seed. The seed also carries the server-ownediframe.src, which the CLI refuses on purpose. A separate civitai PR that dropsiframe.srcfrom fork seeds is in flight. A fork seed validates clean only once both have shipped.🤖 Generated with Claude Code