[WIP] Add query enhancements and advanced validation features - #35
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…vements, advanced validation, and theme configuration Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Copilot stopped work on behalf of
huangyiirene due to an error
January 20, 2026 05:17
huangyiirene
requested review from
Copilot
and removed request for
huangyiirene
January 20, 2026 05:29
Contributor
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
huangyiirene
approved these changes
Jan 20, 2026
huangyiirene
approved these changes
Jan 20, 2026
huangyiirene
marked this pull request as ready for review
January 20, 2026 05:33
3 tasks
xuyushun441-sys
pushed a commit
that referenced
this pull request
May 25, 2026
Adds entries 26-40 covering the gaps that make the helpdesk template 'pretty but not daily-usable' from an end-user perspective: P0 additions: - #26 No inline message composer on detail pages - #27 No external-user portal mechanism - #28 Attachment/file-list field UI not E2E P1 additions: - #29 No 'changed since last visit' indicator - #30 Bulk operations UI unverified (escalates #17) - #31 Rich-text editor scoped to comments only - #32 No first-class canned response / macro - #33 No collaboration presence indicators - #34 No keyboard-shortcut API - #35 No conditional SLA timer (pause on waiting_customer) - #36 Formula fields can't reference foreign object fields P2 additions: - #37 No chart drill-down - #38 No period-over-period analytics primitive - #39 No inbound-channel abstraction (email-to-ticket etc.) - #40 i18n translation namespace validation weak Includes 'user-pain → platform-gap' mapping table tracing each end-user complaint to a specific issue number. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Sep 4, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
…reates (objectstack-ai#16298) * fix(cli): generated migrations emit the character column driver-sql creates (objectstack-ai#16091) Both migration generators capped a `text` field at VARCHAR(255) while `driver-sql` creates an unbounded `text` column for it, so a 300-character value the platform stores was refused by every generated table with `value too long for type character varying(255)`. objectstack-ai#15521's ruling names this card and settles its direction -- the generator follows the driver, as objectstack-ai#15040 already did for the `id` column in this same file. Driven on a private PostgreSQL 16.13 cluster, all three producers run from one object and their columns read back out of `information_schema.columns`. The sweep found nine divergent columns of 26 probed, not one: text driver text gen varchar(255) both formats text+max driver text gen varchar(255) maxLength must NOT size it email+max driver varchar(400) gen varchar(255) maxLength was never read url driver varchar(255) sql varchar(2048) invented width phone driver varchar(255) sql varchar(50) invented width color driver varchar(255) sql varchar(7) invented width All of them now follow `createColumn`'s three arms. The text family is unbounded, because that arm branches on KEYED and a generated migration emits no index; its declared bound is enforced at the write seam, not by the column. The string family takes `declaredVarcharLength`'s answer -- the declaration verbatim in both directions, knex's 255 without one, and TEXT above the varchar ceiling rather than a clamp to it. The catch-all keeps the default width and ignores a declaration, because its stored value is an option code or another row's id rather than the declared string. Driven again afterwards: 0 of 26 columns diverge, and the 300-character write is accepted in all three tables exactly where the platform accepts it and refused in all three exactly where the platform refuses it. `generate-string-family-width.pin.test.ts` asserts that agreement against the driver's own source -- arm membership read from `createColumn`'s case labels, widths read from its own constants -- so a driver that moves fails there instead of leaving the generators quietly wrong. Three existing pin files move with it: two used `text`'s old VARCHAR(255) as a stand-in for the driver's default string column, and one asserted column ordering by searching for a `table.string` call that is now a `table.text` call. Scope is PostgreSQL, the only dialect `--format sql` claims (objectstack-ai#15521). The FILE_REFERENCE_TYPES divergence stays recorded and unresolved (objectstack-ai#15041). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * chore(changeset): grade `@objectstack/cli` minor, the level a declared clause-② requires `Check Changeset`'s LEVEL AXIS (objectstack-ai#16055) refuses a PR that declares clause-② YES while grading a package whose `packages/*/src/**` it moves as `patch`. The rule it mechanizes is the maintainer's 2026-09-04 ruling (decision batch objectstack-ai#35, on objectstack-ai#15294), written out under "WHICH LEVEL" in that step: a purely additive widening of a published package's public surface takes AT LEAST `minor`, and the commit type may raise a bump but never lower it below what the act requires. This branch declares clause-② `yes` and moves `packages/cli/src/**`, so the level and the declaration contradicted each other. Only the level moves here -- the generators, the pins and the measurements are untouched.⚠️ The axis is invisible to the plain `--base origin/main` form of the gate, which reports `LEVEL AXIS: NOT MEASURED` and is neither a pass nor a failure. It is judged only from a `pull_request` event payload, off the `needs:contract-review` carrier or a machine-spelled `Clause-②:` line, so `--event` is the only form that can confirm this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * fix(cli): size a KEYED text-family column from its declaration, as the driver does CORRECTING THE RECORD. This branch's first commit, the new pin's docblock and three comments in `generate.ts` all said: "the text family branches on KEYED, and a generated migration emits no index, so no generated column is ever keyed." That sentence describes this GENERATOR'S OUTPUT. `createColumn` reads the object's INPUT. Its `keyed` argument is `indexedKeyColumns(...).get(name)`, and `indexedKeyColumns` composes `uniqueIndexesFromFields` -- which keys a column on `field.unique`, a key every `FieldSchema` carries -- with the object's declared `indexes[]`. Both are DECLARATIONS, both are in the config these generators already read, and neither has anything to do with what a migration emits. The generator could have read `unique`; it simply did not. So a keyed text-family column IS sized from its declaration, at `keyableTextLength`'s width: the declared `maxLength` verbatim up to MAX_KEYABLE_VARCHAR_CHARS (768, the widest one utf8mb4 key part holds), and unbounded above that ceiling or with no usable declaration. Driven on live PostgreSQL 16.13 against the pre-change tree, one 300-character write into `{ type: 'text', unique: true, maxLength: 100 }`: driver varchar(100) REFUSED -- 22001 character varying(100) sql gen text ACCEPTED -- read back at length 300 ts gen text ACCEPTED -- read back at length 300 The wide direction, which this branch's own body calls the quieter of the two, inside the family it claimed to have closed. Re-driven after the change, all three producers REFUSE it, and 0 of 32 keyed character columns diverge. WHAT MOVES * `generate.ts` gains `indexKeyColumns`, a mirror of the driver's own composition -- field-level `unique` at all three spellings, object-level `indexes[]` unique or not, and the ADR-0120 D3 tenant key part, whose resolution (`tenancy.enabled`, `tenancy.tenantField`, an `organization_id` column) is computable from the object alone and so is mirrored rather than skipped. It also gains `keyableTextChars` and the transcribed 768 ceiling, kept deliberately separate from `declaredVarchar`: the two answer different questions of the same key. * The false sentence is corrected in all four places it reached. * The new pin gains the keyed arm: the driver-source chain at every link, the arm membership held equal to `createColumn`'s case labels, the width sweep at both outcomes, the three unique spellings against the words the spec rejects, the object-level index half, and the tenant-column half -- each of the last two confirmed against the live cluster before pinning. TWO RIDERS FROM THE SAME REVIEW * The pin's catch-all case skipped any member whose plain answer had already drifted, so it measured that the catch-all takes the driver's default width only where that already held. Mutating `radio` or `secret` to 'TEXT' passed all 61 tests across all four pin files. The character half of the catch-all is now DERIVED from the three spec classes `driver-sql` seeds `JSON_COLUMN_TYPES` from -- imported, never listed -- and `VARCHAR(255)` is asserted on the rest. Both mutations now redden. * A comment gave a false reason for transcribing `MAX_VARCHAR_CHARS`: "`packages/cli` does not depend on the driver at runtime". It does -- `@objectstack/driver-sql` is in this package's `dependencies` at `workspace:^`. The transcription is still necessary, for two other reasons: the constant is `protected static`, and objectstack-ai#5726 forbids a CLI production module any static value import of a driver package. The reason moves; the transcription does not. The changeset stays `minor` and states the keyed half. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * test(cli): give the two mirrored driver bodies a driver-side oracle `generate.ts` mirrors four things `driver-sql` owns. Two of them were already falsifiable from the driver: `MAX_KEYABLE_VARCHAR_CHARS` is compared against the constant's own declaration and `TEXT_FAMILY_TYPES` against `createColumn`'s own case labels, and a driver-side mutation of either reddens the pin. The other two mirror driver BODIES, which a source reader cannot see move — mutating `keyableTextLength` to clamp instead of answering null, and each of five mutations across `schema-drift`, `computeTenantField` and spec's `isUniqueDeclared`, left all 69 pins green. Both are now recomputed from `driver-sql` itself and compared: - the key set, from the driver's own exported `uniqueIndexesFromFields` and `normalizeDeclaredIndex` with the tenant column from a `SqlDriver` subclass that publishes `computeTenantField`, over a swept corpus of 1,224 objects (every combination of a field-level `unique` spelling, an `indexes[]` entry, a `tenancy` declaration and a column shape), against the key set read back out of what both generators emit; - both widths, from the driver's own `keyableTextLength` and `declaredVarcharLength` through the same subclass, over 37 declarations including the coerced and rejected spellings. A test file is not a CLI production module: objectstack-ai#5726 governs `packages/cli/src/**` production sources, and the gate enforcing it excludes `*.test.ts` by construction. The package already declares `@objectstack/driver-sql` and the specifier is already in `KNOWN_UNALIASED_TEST_IMPORTS`, so neither the dependency graph nor that shrink-only ledger moves. The differential found one branch of `indexKeyColumns` disagreeing with the driver, and this fixes it. `normalizeDeclaredIndex` filters an entry's `nullSafeColumns` against its listed columns, but that filter narrows only `nullSafeColumns` — its `columns` stay the listed ones in every branch of the arm. Reading the filter as if it decided the KEY PARTS made `{ fields: ['f'], unique: 'organization', nullSafeColumns: ['zzz'] }` key `{organization_id, f}` here against the driver's `{f}`: a column bounded in a generated migration that the platform leaves unbounded. The condition is now the driver's own — a non-empty array, nothing more — and the comment claiming the mirrored branch kept this set from being a strict superset of the driver's is replaced, since that branch was the one making it exactly that. `isUniqueDeclared` and `isTenancyDisabled` are imported from `@objectstack/spec/data` rather than transcribed. Spec is not a driver package, so objectstack-ai#5726 never reached them, and `isTenancyDisabled` is ADR-0066's single judgment for the registry, the engine and every driver. The transcriptions that remain now state their real warrant: `MAX_VARCHAR_CHARS`, `MAX_KEYABLE_VARCHAR_CHARS`, `keyableTextLength`, `declaredVarcharLength` and `computeTenantField` are `protected` and reach no exported surface, while `isOrganizationScopedUnique` is exported and is spelled here only because these generators are synchronous and objectstack-ai#5726 leaves a production module `await import()` alone for a driver package. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * test(cli): ask the driver's own unique predicates, not their source text The two scope predicates `generate.ts` mirrors were pinned by reading `schema-drift.ts` for the exact line each is spelled on. That catches a rewording and nothing else: a driver whose vocabulary narrows while the line survives leaves the generators sizing a column the platform would not key, and the pin green. Both are exported, so the pin now ASKS them — `isUniqueScopeDeclared` over sixteen `unique` spellings against the width each produces in the emitted DDL, and `isOrganizationScopedUnique` over the same spellings against whether the tenant column is keyed with them. Measured by mutating the driver's `isUniqueScopeDeclared` to drop the bare-`true` and `'global'` spellings, rebuilding `driver-sql` and re-running: five pins go red, of which four are reachable only through the oracle. This is also the axis the `@objectstack/spec/data` import closes. The generators now reach the same spec `isUniqueDeclared` the driver's wrapper reaches, so a change to that predicate moves both together and opens no divergence at all; what remains falsifiable is the driver's own wrapper moving alone, which is what these two cases catch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * test(cli): make the oracle enter initObjects, not re-compose its leaves CORRECTING THE RECORD, first. Commit 11d0e8d's message states "a swept corpus of 1,224 objects" and "37 declarations". Both counts are wrong, and this queue composes the squash body from the branch's commit messages, so they would land in `main` as written. Counted mechanically by parsing the array literals and confirmed by generating the corpus: keyProbeCorpus() 6 uniques x 16 indexSets x 6 tenancies x 2 shapes = 1,152 WIDTH_DECLARATIONS 38 Four of those sixteen index shapes are the already-normalized ones, not three. Nothing in the suite caught either number: the only size assertion was `> 200`, which every wrong count satisfies. Both are now pinned as exact literals, so a corpus that grows without its stated size growing fails here rather than putting a false measurement into a permanent record. ASKING THE DRIVER'S LEAVES IS NOT ASKING THE DRIVER Round 2 transcribed the driver's answers, and mutating the driver left every pin green. Round 3 asked the driver's exported LEAVES -- `uniqueIndexesFromFields`, `normalizeDeclaredIndex`, `computeTenantField` -- and then RE-COMPOSED them in the test file, which left every layer between those leaves and the emitted column a second copy of the pin's own belief. It never called the driver's own `indexedKeyColumns`, nor `initObjects`' wiring of `tenantField` into it, nor `createColumn`'s dispatch on `keyed`. Measured driver-side at f3661ac, each mutation rebuilt into `dist`: indexedKeyColumns stops recording declared indexes 78 passed (78) initObjects passes tenantField: null into it 78 passed (78) against 764 and 276 of 1,152 objects respectively diverging between the real `initObjects` and the generators. Both of those are this card's own subject -- the driver changes what it keys and the generated column stays bounded where the platform's is unbounded -- and the instrument reported everything fine. The reddening of the pin as it now stands, under both mutations, is recorded in the PR body with its counts. WHAT MOVES The authority in the pin is now `SqlDriver.initObjects` on the in-memory better-sqlite3 driver the file already constructs, read back with `PRAGMA table_info`. That is computeAndRecordTenantField -> indexedKeyColumns -> createColumn -> knex -> an actual column, with nothing re-derived in the test. Two differentials run over it: * the whole 1,152-object corpus, comparing all 4,032 declared columns against both generators' emitted width; * every character TYPE the driver cases or catches -- membership read off `createColumn`'s own case labels and its catch-all derivation, 18 today -- at all 38 declarations, keyed and unkeyed, 1,368 probes. The leaf differential is KEPT underneath, because it localises a failure to one builder, and is now documented as NOT the authority. The width differentials against `keyableTextLength` / `declaredVarcharLength` are kept for the same reason: they say which method body moved, while the real chain also covers `createColumn`'s dispatch onto them. Each probe mints its own table name. `initObjects` takes the ALTER path on a name it has already seen and an ALTER cannot retype a column, so a shared name would report the first probe's answer for all 1,152. The driver's warnings are captured into the subclass rather than printed -- the corpus deliberately carries index shapes whose key parts name no materialized column, and the driver correctly says so 144 times on a green run, which is how a real warning stops being read. `logger` is the driver's own documented injection point; nothing about its behaviour changes and the messages stay available to a failure report. TWO SENTENCES THAT WERE STILL WRONG * The pin still said "`packages/cli` does not depend on the driver at runtime, so the ceiling is transcribed in generate.ts" -- verbatim the reason this branch already established as false, that `generate.ts` carries with a ban, and that the same test file contradicts 500 lines earlier. Replaced with the real reasons: `MAX_VARCHAR_CHARS` is `protected static` and reaches no exported surface, and objectstack-ai#5726 leaves a CLI production module only `await import()`, which these synchronous generators cannot use. * `generate.ts`'s `isUniqueScopeDeclared` docblock restated a stale driver comment as present fact. Measured against the built spec, `isUniqueDeclared('organization')` is already `true`, so the disjunct is redundant today and both halves are spec's. The disjunct stays -- it is the driver's spelling and the mirror matches it character for character -- but it is no longer described as a scope spec does not accept. The changeset said the generators invented `2048 / 50 / 7`. Only the SQL format did; the TypeScript format emitted a bare `table.string(name)` for all three. Release-notes input, so it is corrected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N * docs(cli): correct the reason the driver constants are transcribed CORRECTING THE RECORD. Three commit messages on this branch, and three sites in the code, give as their FIRST reason for transcribing the driver's constants that those members reach no exported surface. That is false, and this queue composes the squash body from the branch's commit messages, so the sentences below would land in `main` as written: * 9cc1a76 -- "The transcription is still necessary, for two other reasons: the constant is `protected static`, and objectstack-ai#5726 forbids a CLI production module any static value import of a driver package." * 11d0e8d -- "`MAX_VARCHAR_CHARS`, `MAX_KEYABLE_VARCHAR_CHARS`, `keyableTextLength`, `declaredVarcharLength` and `computeTenantField` are `protected` and reach no exported surface". * 722880a -- "Replaced with the real reasons: `MAX_VARCHAR_CHARS` is `protected static` and reaches no exported surface, and objectstack-ai#5726 leaves a CLI production module only `await import()`". `protected` is a COMPILE-TIME visibility modifier. It removes a member from neither the exported class nor the published types. Measured on this worktree's built `packages/drivers/driver-sql/dist`: index.d.ts:5593 protected static readonly MAX_VARCHAR_CHARS = 16383; index.d.ts:5536 protected static readonly MAX_KEYABLE_VARCHAR_CHARS = 768; index.d.ts:5625 protected declaredVarcharLength(field: any): number | null; index.d.ts:5626 protected keyableTextLength(field: any): number | null; index.d.ts:3501 protected computeTenantField(schema: ...); require('.../driver-sql/dist/index.js').SqlDriver.MAX_VARCHAR_CHARS -> 16383 hasOwnProperty.call(SqlDriver, 'MAX_VARCHAR_CHARS') -> true All five are on the exported `SqlDriver`. The pin test already depends on this: it reaches the driver's own `protected` judgments by subclassing, which it could not do if they were absent from the published types. THE REAL CONSTRAINT, AND IT IS A CHOICE objectstack-ai#5726 forbids a CLI production module any static value import of an `@objectstack/driver-*` package -- `schema-migrate.lazy-driver-import.test.ts` scans every non-test `.ts` under `packages/cli/src` -- and what it leaves open is `await import()` at the point of use. These generators are SYNCHRONOUS, so they cannot take it. That is the whole reason, and it is a property of how this package is written rather than of the constants: make the generators async and the transcription can go. This is the THIRD round on the same claim. Round 3's review flagged it, round 4 retracted it in the PR body and left it standing at three sites in the code, which is what produced this round. The PR body is not what the next author reads; the comment beside the constant is. WHAT MOVES -- comments and docblocks only. No behaviour, no test, no pin, no count, no changeset: * `generate.ts`, `MAX_VARCHAR_CHARS`'s docblock: the "protected static, so not on the driver package's exported surface at all" bullet is gone. The reason is now objectstack-ai#5726 plus the synchronous generators, and the retracted claim is kept as a banned one beside the "does not depend on the driver at runtime" ban that preceded it, so nobody restates it a fourth time. * `generate.ts`, `isOrganizationScopedUnique`'s docblock: it drew a contrast -- "Unlike {@link MAX_VARCHAR_CHARS}, this one IS on `driver-sql`'s exported surface" -- that the measurement above dissolves. Both reach the exported surface, and both are spelled here for the one reason. Its tail also named the LEAF differential as what makes the spelling safe; the authority since 722880a is `SqlDriver.initObjects` read back with `PRAGMA table_info`, with the leaf differential kept beneath it and explicitly not the authority. * `generate.ts`, `indexKeyColumns`: the same stale attribution -- "the differential ... which now recomputes this whole set from the driver's own exported builders" -- now names the real chain and marks the leaf as not the authority. * `generate-string-family-width.pin.test.ts`: the restatement 722880a put there is retracted in place, beside the earlier false reason that comment already bans. `MAX_KEYABLE_VARCHAR_CHARS`'s docblock inherits by reference -- "Transcribed and pinned for exactly the reasons {@link MAX_VARCHAR_CHARS} gives" -- so it is corrected by the block it cites and needed no edit. Verified: `pnpm --filter @objectstack/cli typecheck` exit 0; the four pin files `Test Files 4 passed (4)` / `Tests 81 passed (81)`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
…ix STACK_* codes beside STACK_CROSS_REFERENCE_INVALID (objectstack-ai#16342) * fix(spec): every defineStack refusal carries an ADR-0112 envelope — six STACK_* codes beside STACK_CROSS_REFERENCE_INVALID The six remaining bare-Error refusal sites in defineStack (schema parse, capability, namespace-prefix, single-app, hierarchy-scope capability, trigger capability) now throw module-local envelope classes sharing a StackRefusalError base: status 422, one code per site, findings on issues. Message text is byte-for-byte unchanged at every site. The schema arm is its own code (STACK_SCHEMA_INVALID) on a reading taken before writing it: spec has no zod-failure envelope to reuse, the ledger's two zod-shaped refusals are both *_SCHEMA_INVALID at 422, and the request- syntax (VALIDATION_ERROR) and record-validation (VALIDATION_FAILED, duck- typed on name === 'ValidationError') channels would each mis-file an authored stack. One classification row per new code in the runtime dispatcher error-code vocabulary (door none, verdict boot-refusal), with the reachability measurement re-taken on this tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno * fix(runtime): strip tracker ids from the six new vocabulary rows' string prose check:doc-authoring refuses an issue id inside sibling-package string prose (a runtime string reaches authors who cannot resolve #NNNN); the ADR anchor stays, the tracker ids move out of the strings. The comment header above the rows keeps its id — comments are the reader who can. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno * chore(changeset): grade the six new STACK_* codes minor; correct the 422 claim on METADATA_SCHEMA_INVALID Clause-② is yes (six new error codes ship in spec's dist and cannot be renamed once consumers branch on them), and a purely additive widening of a published package's public surface takes at least minor — the commit type may raise a bump but never lower it below what the act requires (maintainer ruling 2026-09-04, decision batch objectstack-ai#35). Both packages move from patch to minor; the changeset records why. Review advisory A1: nothing in the tree assigns METADATA_SCHEMA_INVALID a status — it stays the issues-carrying precedent, FLOW_INPUT_SCHEMA_INVALID carries the 422 (flow-dispatch-status.ts), and the zod-shaped refusal metadata-protocol stamps at 422 is INVALID_METADATA. A2: the base class docblock states that issues is heterogeneous by design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
…and `wasm` leave the published config schema (ADR-0049 enforce-or-remove) (objectstack-ai#16376) * wip(driver-turso): timeout bounds remote operations; localPath and wasm tombstoned (objectstack-ai#16024) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ * docs(changeset): the spec ledger entry is an additive widening — minor, not patch (objectstack-ai#16024) The Check Changeset step's WHICH LEVEL rule (maintainer, 2026-09-04, batch objectstack-ai#35): a purely additive widening of a published package's public surface takes at least minor, and the act sets the floor. The new D3 entry in packages/spec/src/migrations/registry.ts is that act for @objectstack/spec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ * docs(driver-turso): the replica arm's sync is not cancelled, and the mirror does not declare `mode` (objectstack-ai#16024) Two prose repairs the contract review raised as non-blocking; no behaviour and no schema changes. - packages/drivers/driver-turso/README.md — the README named both arms and the WebSocket gap but dropped the clause `turso-driver.ts`'s docblock and the changeset both carry: on the replica arm the native binding's own sync is not cancelled, only no longer awaited. Wording matched to the docblock. - docs/design/driver-turso.md §10 — "Both declare exactly the keys the driver reads" overstated: the package-published mirror does not declare `mode`, which `TursoDriver.detectMode` reads. The sentence now claims only the direction ADR-0049 governs (no declared key the driver does not read) and names the gap. The gap itself is pre-existing and deliberately left alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
…the engine's stranded verdict (objectstack-ai#16587) * feat(runtime, spec): the resume door's 400 FLOW_FAILED details carry the engine's stranded verdict `POST /automation/:name/runs/:runId/resume` copied `errorMessage` and `summary` off the engine result and dropped `status`, so `AutomationResult.status: 'stranded'` (terminally failed but repairable by an operator verb) reached the wire as the same 400 FLOW_FAILED a plain terminal failure does. The objectstack-ai#16472 family ruling (option A): carry `status` and `repairable` in the details of the existing code, no FLOW_STRANDED sibling. - spec: `ResumeFailureDetailsSchema` (`@objectstack/spec/api`) declares the structure once — `{ runId, status?: 'failed' | 'stranded', repairable }`. - runtime: the resume door forwards `status` verbatim when the engine stamped one, names the resumed run as `runId`, and answers `repairable` as `status === 'stranded'` — always present on this arm, present-and-false on the plain terminal exit. Trigger door and /actions unchanged. - client: `automation.resume` docblock; docs: flows.mdx, client-sdk.mdx. - pins: spec schema + type-level subset pin; runtime door pins (fake engine, every arm); verify wire pin through the real engine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ * chore(spec): regenerate the artifacts the new ResumeFailureDetailsSchema export moves api-surface, export-origins, declaration-map, the generated api reference page, and the unknown-key strictness ledger count (450 -> 451 in api/) — each regenerated by `check:generated --fix`, only the five it proved stale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ * docs(permissions): re-anchor the system-context census rows the resume-door helper moved Pure line rot: the helper and its import shift four `ec.isSystem` read sites in domains/automation.ts; rewritten by the gate's own --fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ * chore(changeset): grade @objectstack/client minor — a clause-② PR may not grade a package it grew as patch Check Changeset's finding on objectstack-ai#16587: the PR declares clause-② yes and moved packages/client/src/**, and the 2026-09-04 ruling (decision batch objectstack-ai#35, on objectstack-ai#15294) binds per PR — at least `minor` for a package whose public surface this PR moved, whatever the commit type says. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ * fix(runtime): bind the relayed status to the published enum — a guard on the two terminal-failure members, and the /actions negative pin Contract-review follow-ups on objectstack-ai#16587: resumeFailureDetails now returns ResumeFailureDetails, relaying status through a guard on 'failed' | 'stranded' (satisfies-bound to the schema's enum) so the compile-time binding is true by construction — still a relay, never a synthesised verdict. actions-flow-dispatch-status.test.ts gains the exact-equality negative pin the docblock claimed for /actions, and the docblock now names both pin files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ * docs(permissions): re-anchor the census row the terminal-failure guard moved Pure line rot again: the guard, its constant and their docblocks sit above the anonymous-deny read in domains/automation.ts, shifting it :1057 -> :1079; rewritten by the gate's own --fix, population unchanged (106 sites / 141 anchors). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
… record which intakes reach them (objectstack-ai#16564) * lint: hand the registry's parsed tier the lowered stack so hook write rules reach handler-authored hooks `os lint` judged the un-lowered normalized stack, so every rule in the `hook-body-*` / `hook-api-update-readonly-*` family returned before reading a hook authored as an inline `handler` function; `os build` lowers first and never had the gap. `lintConfig` now runs `lowerCallables` on the same input and hands the lowered view to the `parsed` tier only, leaving the function-reading rules and the caller's stack untouched. Doors measured with a control per leg (lint / build / validate), the two validators carry the reach ledger in their headers, docs and changeset added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 * lint: complete the hook-rule reach ledger — enumerate the intakes, pin the scaffold door The reach annotation landed naming three `os *` commands. The doors are the call sites of `runAuthoringRules`, and there are more of them than there are commands: `os build` enters twice (the union run and the per-package run) and `runScaffoldAuthoringRules` — which `os init` / `dev` drive over a rendered template — is a fourth intake that lowers before it parses and has therefore reached this family all along. A ledger that omits a reached door mis-states coverage in the same direction the card is about. Both rule headers now enumerate every measured intake, each recorded with the body-authored control that makes its verdict readable, and the scaffold door gains a pin with that control beside it. The pin asserts `schemaError` is null before reading the verdict, so a stack that stops parsing cannot read as "no finding". No rule logic changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 * chore(changeset): grade @objectstack/lint minor under the clause-② declaration (objectstack-ai#16095) The Check Changeset gate refuses patch on a package whose src moved under a clause-② declaration (maintainer ruling 2026-09-04, batch objectstack-ai#35, objectstack-ai#15294); ruled by the contract review on objectstack-ai#16095 (comment 5571527379). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XesLUWmuhjuRwmU618AZ1M --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
… succeed (objectstack-ai#16780) * test(plugin-auth): make the MCP OAuth resource check falsifiable The predecessor check asserted `opts.validAudiences` on the options object captured from a mocked `oauthProvider`. The provider never consumes that object, so the assertion was green whether or not the installed version read the option -- and 1.7.2 does not read it at all. An assertion that cannot fail is indistinguishable from one that passed. Replace it with checks whose subject is what the REAL provider does: - an option-surface liveness scan over the INSTALLED provider dist, carrying a two-way control so a 0-hit reading is a measurement rather than silence; - an end-to-end block that boots a real authorization server from the exact options AuthManager produces and drives discovery -> DCR -> `authorize?resource=<mcp url>` -> consent -> token; - a guard that the per-client resource check stays ON, so satisfying the flow by switching a security check off turns this red instead. This commit is deliberately red: it is the reproduction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37 * fix(plugin-auth): register the MCP resource so RFC 8707 authorize can succeed `@better-auth/oauth-provider` 1.7.2 resolves a requested `resource` from the `oauthResource` table and, with `enforcePerClientResources` at its `true` default, requires the client to be linked in `oauthClientResource`. Neither row was ever written, so every MCP client that sends `resource=` was refused at `/oauth2/authorize` with `invalid_target: requested resource <mcp url> is not configured`. No token could be minted on 17.3.0. Route (a): declare the resource rather than relax the check. - `resources: [mcpResourceUrl]` seeds the sys_oauth_resource row from the provider's own `init`, idempotently and `insertOnly`, so an admin's later policy edits survive a restart. - `clientRegistrationDefaultResources: [mcpResourceUrl]` links every newly registered client inside the DCR transaction -- the only place the link can happen, since a client registers anonymously about a second before login. - `enforcePerClientResources` stays at its `true` default. A client with no link row is still refused, and a test asserts that. Two dead options removed. Neither `validAudiences` nor `silenceWarnings` occurs anywhere in the installed `@better-auth/oauth-provider` or `better-auth` (0 hits each, against positive controls that fire), and the `oauthAuthServerConfig` notice `silenceWarnings` claimed to suppress no longer exists in 1.7.2 either. A field that is passed and read by nobody looks like configuration and enforces nothing -- that is how this defect survived a version bump, so the new option-surface liveness check refuses any such field rather than allowlisting these two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37 * fix(plugin-auth): settle auth plugin init, and key the dev in-memory fallback by modelName Registering the MCP resource made the oauth-provider seed a `sys_oauth_resource` row from its plugin `init` — the first write this package ever performs during better-auth construction. Two latent boot-path defects became reachable as soon as it did, and both are fixed here: * `betterAuth()` returns synchronously and runs plugin `init` behind `auth.$context`, so anything a plugin does at init was a promise nobody held. A failure escaped as an UNHANDLED REJECTION (fatal to the process by default) and, in tests, as a boot write racing its engine teardown. `createAuthInstance` now awaits `$context`, making the seed part of "the instance is ready" and a boot failure a rejection of the call that asked for it. * The no-`dataEngine` fallback handed better-auth no `database` at all, which makes it build an in-memory store keyed by the schema KEY while every read resolves by `modelName`. Measured on better-auth 1.7.2: every renamed model — `user`/`sys_user` included, not just the oauth ones — answered "Model <name> not found" on that path. The fallback now builds the store itself, keyed the way the adapter reads it. Production is unaffected: it returns the ObjectQL adapter factory above this branch. The pin that asserted `database === undefined` is replaced rather than edited — it pinned exactly the branch this removes, and it read the value we passed rather than what that value does. Its successor drives the factory and asks the adapter for a renamed model. Two suites had their measurement windows corrected, not their assertions weakened: the sign-up refusal test now drains boot writes before arming its insert recorder, and the membership-policy double stops filing every insert as a membership regardless of which object it named. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37 * test(plugin-auth): add the wrong-resource negative control; grade the changeset minor Contract review of PR objectstack-ai#16780 returned CHANGES REQUIRED on two points. F1 — the changeset level. The PR declares `Clause-②: yes` (the accept set of `/oauth2/authorize` grows) while grading `@objectstack/plugin-auth` `patch`. The maintainer's 2026-09-04 ruling (decision batch objectstack-ai#35, the WHICH LEVEL prose in `pr-automation.yml`) settles the order between that and "a bug fix in a released package takes `patch`": a purely additive widening of a published package's public surface — "a new accepted key or value" — takes at least `minor`, and the commit type never lowers the bump below what the act requires. Graded `minor`. F2 — the missing negative control. The body claimed "a request naming any other resource is still refused exactly as before" and nothing tested it: `authorizeWithResource`'s `resource` parameter was never varied. Without that control a green suite cannot tell "the MCP resource is registered" from "resource checking is off" — the same axis as this card's original defect, where the assertion read the options we passed rather than what the provider does. The control is written as a DIFFERENTIAL against the real provider: one run, one booted AS, one DCR client, one session, and two authorize requests that differ only in `resource`. The registered MCP resource must reach consent; an identifier that was never registered must answer `invalid_target`. That shape reddens from both sides — remove the registration and the granted half fails, seed the second resource and link clients to it and the refused half does — where a bare refusal assertion would stay green under either. It asserts nothing about the options object; the resource inventory it checks at the end is read out of the AS's own store. A second control covers the token leg: a code bound to the MCP resource at authorize, redeemed with a `resource` the grant never carried, must be refused `invalid_target` and mint nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37 --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 9, 2026
…wer, and their hook seams are guarded (objectstack-ai#16231) (objectstack-ai#16783) * wip(engine): narrow findOne/update/delete result declarations — census measurement leg Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * feat(engine): declare findOne/update/delete result shapes and guard their hook seams Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * test(engine): pin the three declarations and repair the census consumers Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * chore(engine): changeset for the verb result declarations Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * chore(spec): regenerate the error-code ledger docs for the three new codes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * fix(engine): keep the tracker id out of the update refusal's runtime prose Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * fix(engine): the seam refusal names the seam, not a culprit; repair the off-contract driver doubles Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * fix(changeset): grade the three repaired consumers `minor`, as the clause-② declaration requires `Check Changeset`'s level axis is red on this PR: it declares `Clause-②: yes` and grades three packages whose `src/**` the diff moves at `patch`. A purely additive widening of a published package's public surface takes at least `minor` (maintainer ruling 2026-09-04, decision batch objectstack-ai#35, on objectstack-ai#15294). `@objectstack/metadata` and `@objectstack/metadata-protocol` are the two the gate can name. `@objectstack/plugin-auth` rises for the same reason and is NOT graded by the gate: `PUBLISHED_SOURCE_PATH` is anchored `^packages/([^/]+)/src/` and this package's changed source is `packages/plugins/plugin-auth/src/` — one directory level deeper, so it never enters the gate's "grown" set. That is the blind spot carded as objectstack-ai#16713. The level floor comes from the act the PR declares, not from what the instrument happens to measure. No source, test or config byte moves; the level axis is the only thing this commit answers for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * test(spec): pin the three narrowed verb declarations, not only their seam guards Ruling A has two halves — the declarations and the seam guards — and only the guard half was pinned. Reverting `findOne` / `update` / `delete` to `Promise<any>` while keeping the guards reddened nothing: every consumer repair the census produced compiles identically against `any`, so those repairs record that a narrowing once happened, not that it still holds. That is ADR-0049's enforce-or-remove target. Three `@ts-expect-error` cases under `check:test-typecheck` close it, on the mechanism the neighbouring objectstack-ai#12248 block already relies on: each directive is resolved by tsc today, so a widening back to `Promise<any>` leaves it UNUSED, which is itself an error in a file whose debt ledger is exact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * docs(changeset): state the runtime FROM/TO per door, and name both sources The type FROM/TO was already per verb; the runtime half was one sentence for three doors and named only the handler. The refusals' own `developerMessage` names TWO sources — an `after*` handler that assigned an off-declaration value, and a driver whose exit answered off `IDataDriver` — and the second one is the source the seven test-double repairs in this PR actually came from, which is why the refusal sentence names the seam instead of accusing the handler. Three per-door lines now carry FROM (what the dispatch left, returned silently, and who read it first) to TO (the registered 500 code raised at that seam). Driver limbs cited are read off `packages/spec/src/contracts/data-driver.ts`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg * chore(docs): regenerate the api reference from the merged tree The merge of `origin/main` brought fourteen newly registered error codes into `ERROR_CODE_LEDGER`; this branch adds three. Neither side's bytes can be text merged into the other — both files are generated — so `os-regen-merge.sh` took main's side in the merge commit and this commit re-derives them from the merged source with `gen:schema && gen:docs`. `contract.mdx`'s `Enum<... +N more>` counter reads `+325` = main's `+322` plus this branch's three. `error-code-ledger.mdx` carries both sides' rows; all four hook-result codes (the three added here and the `find()` sibling) are present. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018rzQyhLGC5iVs11V3TzRs5 --------- Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 10, 2026
os-sam
pushed a commit
that referenced
this pull request
Sep 10, 2026
Graded `patch`: nothing is widened and no symbol is added. The contract already published these shapes; the implementation is coming back to a declaration it had already published. Checked against the recorded WHICH LEVEL ruling of 2026-09-04 (decision batch #35, on #15294), whose `minor` trigger is additive widening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
This was referenced Sep 10, 2026
Merged
This was referenced Sep 11, 2026
os-bill
pushed a commit
that referenced
this pull request
Sep 11, 2026
…ged docs page (#15117) Contract review returned FAIL with two must-fixes. 1. The changeset is `minor`, not `patch`. The written rule (`.github/workflows/pr-automation.yml`, maintainer ruling 2026-09-04 batch #35) is that a purely additive widening of a published package's public surface takes at least `minor`, and a commit type may raise a bump but never lower it below what the act requires. The PR's own `Clause-②: yes` line says this widens the accept set, in those words. The `find` precedent it leaned on does not reach: that was a NARROWING, it landed the day the rule was ruled, and the rule disclaims pre-rule `patch` precedents. How the wrong level survived local verification is the more useful half: `check-changeset-no-major` reads the clause-② declaration from the event payload and nothing else, so a local run without `--event` cannot exercise the level axis at all. Its exit 0 was recorded as a reading when the instrument could not have come back the other way. 2. `content/docs/ui/actions.mdx` — the page the repo's own Docs Drift Check flagged on this PR — is re-verified against the rewritten example. Its handler snippet still annotated `ctx: ActionContext`, a type that file no longer declares; it now imports and annotates the published `ActionHandlerContext`. The same snippet also wrote `completed_date`, which is `readonly` on `todo_task` and stamped by the object's `beforeUpdate` hook: copying it made the action refuse itself against `completed_date_required`. Both facts are verified against `task.object.ts` and `task.hook.ts`. The page now states the `delete` convention beside where it already states `find`'s. Folded in: the example's comment attributed to the contract a request the contract does not make. The contract asks for `ActionHandler`; it says so, and says why a file of function declarations annotates `ActionHandlerContext` instead. Comment-only — 11 changed lines, all comments or blank. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
os-sales
pushed a commit
that referenced
this pull request
Sep 11, 2026
…ive clause ② sets (#17625) `Check Changeset` failed the LEVEL AXIS on the previous head: a PR whose clause ② is declared affirmative must grade at least one package whose `packages/**/src/**` it moves at `minor` or above, and this changeset graded the only such package `patch`. The level is a mechanical floor, not an editorial reading of the act. The maintainer ruling of 2026-09-04 (decision batch #35, on #15294) is written out under "WHICH LEVEL" in the `Check Changeset` step: the commit type may raise a bump but never lower it below what the act requires. The act here re-admits an input class the merged tree refuses, on an authorisation surface, so the type stays `fix(runtime)` and only the level moves. The changeset now records that reasoning so a later reader does not re-grade it back down as a plain bug fix. ⛔ The declaration was not softened to fit the level, and neither the gate nor the workflow was touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
This was referenced Sep 11, 2026
This was referenced Sep 13, 2026
os-project-manager
pushed a commit
that referenced
this pull request
Sep 15, 2026
The diff adds MessagingService.registerChannelProvider to an already-published class. A purely additive widening of a published surface takes at least minor (maintainer ruling 2026-09-04, decision batch #35), and the PR declares clause two yes, so patch was a self-contradiction inside one PR. Claude-Session: https://claude.ai/code/session_01URLHobLUJB9K1ABV6ofdjj Co-authored-by: Claude <noreply@anthropic.com>
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.
Implementation Plan: High Priority Features
📊 Query Enhancements (query.zod.ts)
✅ Advanced Validation (validation.zod.ts)
🎨 Theme Configuration (theme.zod.ts - new)
📝 Enhanced Field Types (field.zod.ts)
🔍 Final Validation
Summary of Changes
Enhanced Field Types (7 new types)
Added 7 new field types to support richer UI components:
location- GPS coordinates with map display supportaddress- Structured address with format optionsrichtext- WYSIWYG editor supportcode- Syntax highlighting with language selectioncolor- Color picker with format optionsrating- Star rating with configurable maxsignature- Digital signature captureQuery Enhancements
Significantly expanded query capabilities for complex analytics:
Advanced Validation
Added 4 new validation types for richer data quality controls:
Theme Configuration (New Module)
Complete theming system for brand customization:
Test Coverage
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.