diff --git a/.agents/README.md b/.agents/README.md index d4fc8d1..84e2f33 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -10,8 +10,6 @@ update the canonical docs first. ## Skills -- [`forms-core-implementation-step`](./skills/forms-core-implementation-step/SKILL.md) - - normal iterative implementation workflow. - [`forms-core-general-checks`](./skills/forms-core-general-checks/SKILL.md) - baseline verification before handoff, PR creation, and review replies. - [`forms-core-self-review`](./skills/forms-core-self-review/SKILL.md) diff --git a/.agents/rules/read-review-contract.mdc b/.agents/rules/read-review-contract.mdc index 05ed856..3f748ee 100644 --- a/.agents/rules/read-review-contract.mdc +++ b/.agents/rules/read-review-contract.mdc @@ -6,6 +6,6 @@ alwaysApply: true # Read Review Contract Start review and implementation work from `REVIEW.md`, then follow -`docs/architecture.md`, `docs/steps.md`, `docs/pr-plan.md`, -`docs/quality-gates.md`, `docs/readme-guidelines.md`, and +`docs/architecture.md`, `docs/pr-plan.md`, `docs/quality-gates.md`, +`docs/readme-guidelines.md`, `docs/release-train.md`, and `docs/review/forms-core-checklist.md`. diff --git a/.agents/skills/forms-core-general-checks/SKILL.md b/.agents/skills/forms-core-general-checks/SKILL.md index 4c041fb..519344c 100644 --- a/.agents/skills/forms-core-general-checks/SKILL.md +++ b/.agents/skills/forms-core-general-checks/SKILL.md @@ -37,7 +37,7 @@ ready, and after addressing review feedback. - `docs/architecture.md`; - `docs/quality-gates.md`; - `docs/review/forms-core-checklist.md`; - - affected step in `docs/steps.md`. + - `docs/release-train.md` for release or publish changes. ## Required Commands diff --git a/.agents/skills/forms-core-implementation-step/SKILL.md b/.agents/skills/forms-core-implementation-step/SKILL.md deleted file mode 100644 index acc4ddb..0000000 --- a/.agents/skills/forms-core-implementation-step/SKILL.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -name: forms-core-implementation-step -description: >- - Implement the next scoped forms-core step by reading the architecture, - current step tracker, PR plan, and review contract; make only the intended - change, validate it, update docs/steps.md, and prepare concise handoff notes. -metadata: - short-description: Implement next forms-core step ---- - -# forms-core Implementation Step Skill - -Use this skill for normal implementation work in `forms-core`. - -## Goal - -Advance exactly one scoped step from `docs/steps.md` without drifting from the -architecture contract. - -## Required Reading - -1. `AGENTS.md` -2. `REVIEW.md` -3. `docs/architecture.md` -4. `docs/steps.md` -5. `docs/pr-plan.md` -6. `docs/review/forms-core-checklist.md` - -## Workflow - -1. Confirm branch and worktree: - - ```bash - git status --short --branch - ``` - -2. Identify the next unchecked step in `docs/steps.md`. -3. Confirm the expected PR scope in `docs/pr-plan.md`. -4. Implement only that scope. -5. Add focused tests for real behavior. -6. Run required validation. -7. Update `docs/steps.md` in the same change. -8. Run self-review before handoff. - -## Stop Conditions - -Stop and report the blocker when: - -- the requested step would require React/UI code; -- a dependency outside the allowed list is needed; -- TanStack form-core lacks a required behavior and the architecture needs a - decision; -- validation cannot run. - -## Output Format - -```text -Implementation step: -- Step: -- Files changed: -- Validation: -- docs/steps.md update: -- Remaining risks: -``` diff --git a/.agents/skills/forms-core-self-review/SKILL.md b/.agents/skills/forms-core-self-review/SKILL.md index 5d43447..2f385df 100644 --- a/.agents/skills/forms-core-self-review/SKILL.md +++ b/.agents/skills/forms-core-self-review/SKILL.md @@ -26,7 +26,7 @@ description when requested, or PR comment. - `docs/quality-gates.md`. - `docs/readme-guidelines.md` when README changes. - `docs/review/forms-core-checklist.md`. -- Affected step in `docs/steps.md`. +- `docs/release-train.md` when release or publish behavior changes. ## Review Steps diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..fb47251 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,22 @@ +name: Publish Package to npmjs + +on: + push: + tags: + - 'v[0-9]*.[0-9]*.[0-9]*' + - 'v[0-9]*.[0-9]*.[0-9]*-*' + +jobs: + publish: + permissions: + contents: read + id-token: write + uses: revisium/revisium-actions/.github/workflows/npm-publish.yml@184b609dcbc1e23f14021141f29226d4e6a0f1ac # v0.3.6 + with: + node_version: 24.11.1 + install_command: npm ci + npm_access: public + publish_auth: token + create_github_release: false + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release-train.yml b/.github/workflows/release-train.yml new file mode 100644 index 0000000..544cf90 --- /dev/null +++ b/.github/workflows/release-train.yml @@ -0,0 +1,50 @@ +name: Release Train + +on: + workflow_dispatch: + inputs: + action: + description: Release train transition + required: true + type: choice + options: + - start-minor-alpha + - start-major-alpha + - start-minor-rc + - start-major-rc + - start-minor-stable + - start-major-stable + - alpha-bump + - promote-rc + - rc-bump + - stable + - patch + - patch-alpha-start + - patch-rc-start + dry_run: + description: Validate and show the computed release without pushing. + required: false + default: false + type: boolean + +concurrency: + group: ${{ github.repository }}-release-train + cancel-in-progress: false + +permissions: + actions: read + contents: read + +jobs: + release-train: + name: Release train + uses: revisium/revisium-actions/.github/workflows/release-train.yml@763f2746000f15842e642130dba3acf09c5d00ac # v0.3.2 + with: + action: ${{ inputs.action }} + dry_run: ${{ inputs.dry_run }} + base_branch: master + node_version: 24.11.1 + install_command: npm ci + validate_command: npm run verify + secrets: + RELEASE_BOT_PRIVATE_KEY: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }} diff --git a/AGENTS.md b/AGENTS.md index 9b91d2f..3b58d2e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ MCP memory when project context must be read or written. - **Docs first.** Before implementing behavior, read and update the matching source-of-truth doc: [docs/architecture.md](./docs/architecture.md), - [docs/steps.md](./docs/steps.md), [docs/pr-plan.md](./docs/pr-plan.md), + [docs/pr-plan.md](./docs/pr-plan.md), [docs/release-train.md](./docs/release-train.md), [docs/readme-guidelines.md](./docs/readme-guidelines.md), [docs/quality-gates.md](./docs/quality-gates.md), or [REVIEW.md](./REVIEW.md). @@ -26,13 +26,7 @@ MCP memory when project context must be read or written. `@tanstack/form-core` and `mobx` unless the docs and user explicitly approve another dependency. Add test dependencies only when the repo lacks a suitable test runner. -- **Update the step tracker.** Every implementation PR must update - [docs/steps.md](./docs/steps.md) with completed checks and remaining work. - The final cleanup PR removes or replaces this temporary tracker after durable - docs, tests, and README cover the result. - **Use repo-local skills when available.** - [`forms-core-implementation-step`](./.agents/skills/forms-core-implementation-step/SKILL.md) - is the normal iterative workflow. [`forms-core-general-checks`](./.agents/skills/forms-core-general-checks/SKILL.md) is the baseline verification workflow. [`forms-core-self-review`](./.agents/skills/forms-core-self-review/SKILL.md) @@ -55,23 +49,15 @@ MCP memory when project context must be read or written. ## Quality Gates -For this docs-only bootstrap phase: - -```bash -git status --short --branch -git diff --check -``` - -After the package scaffold lands, update this section and -[docs/quality-gates.md](./docs/quality-gates.md), then use the repository -scripts. The intended full gate is: +Use the repository verification script before handoff, PR creation, or review +replies: ```bash npm run verify ``` -The planned `verify` script should run TypeScript, lint, unit tests, build, and -Markdown/skill checks. +The `verify` script runs Markdown/skill lint, formatting, TypeScript, ESLint, +unit tests with coverage, and package build. Sonar has zero-tolerance semantics for pull requests in this repo. A passing PR Quality Gate is not enough by itself: always inspect unresolved Sonar issues for @@ -84,5 +70,5 @@ only when the repo permits it. ## Handoff Start from [docs/handoff/README.md](./docs/handoff/README.md). It links the -implementation steps, review contract, architecture, and reusable bootstrap -pattern for other libraries. +review contract, architecture, quality gates, release docs, and reusable +bootstrap pattern for other libraries. diff --git a/README.md b/README.md index 51398ef..d7d97a7 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,307 @@ # forms-core -Headless TypeScript form library for Revisium projects. - -`@revisium/forms-core` will wrap `@tanstack/form-core` with a MobX-native public -API. It is intentionally not a React package: forms must live outside component -lifecycle, and consumers should observe public getters with MobX `computed`, -`reaction`, and `autorun` without subscribing to TanStack stores directly. - -## Current Status - -This repository is in active implementation. The package scaffold, local -toolchain, CI/Sonar wiring, private MobX selector bridge, scalar form/control -wrappers, validation adapter, explicit server-error lifecycle, nested path -controls, stable-id arrays, patch emission, and disposal hardening are in place. -Final README/API polish is tracked in the implementation steps. - -## Start Here - -- [AGENTS.md](./AGENTS.md) - coding-agent boot rules. -- [REVIEW.md](./REVIEW.md) - review contract for humans, bots, and agents. -- [docs/architecture.md](./docs/architecture.md) - target architecture. -- [docs/steps.md](./docs/steps.md) - temporary implementation tracker. -- [docs/pr-plan.md](./docs/pr-plan.md) - intended PR sequence. -- [docs/release-train.md](./docs/release-train.md) - npm release plan. -- [docs/readme-guidelines.md](./docs/readme-guidelines.md) - README standard - for the public package. -- [docs/quality-gates.md](./docs/quality-gates.md) - local, CI, Sonar, and - package checks. -- [docs/handoff/README.md](./docs/handoff/README.md) - handoff entry point. - -## Package Goals - -- Use `@tanstack/form-core` as the form engine. -- Provide a headless MobX-reactive API. -- Keep React, Chakra, and UI components out of scope. -- Keep TanStack store and error-map details private. -- Support scalar controls, nested paths, stable-id arrays, validation, server - errors, patches for autosave, reset, submit, and disposal. +Headless TypeScript forms over `@tanstack/form-core` with a MobX-native public +API. + +[![CI](https://github.com/revisium/forms-core/actions/workflows/ci.yml/badge.svg)](https://github.com/revisium/forms-core/actions/workflows/ci.yml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=revisium_forms-core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=revisium_forms-core) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) + +## Why + +`@revisium/forms-core` is for application state layers that need forms outside +React component lifecycle. TanStack Form Core owns the form engine; this package +adapts its stores into MobX-observable public getters so consumers can use +`computed`, `reaction`, and `autorun` without subscribing to TanStack stores. + +The package is headless. It does not export React components, Chakra wrappers, +or UI-specific hooks. + +## Install + +```bash +npm install @revisium/forms-core @tanstack/form-core mobx +``` + +## Quick Start + +```ts +import { reaction } from 'mobx'; +import { createForm, field } from '@revisium/forms-core'; + +const form = createForm({ + defaultValues: { + email: '', + password: '', + }, + fields: { + email: field({ + validators: { + onChange: ({ value }) => + value.includes('@') ? undefined : 'Invalid email', + }, + }), + password: field(), + }, +}); + +const disposeReaction = reaction( + () => form.controls.email.value, + (email) => { + console.log('email changed', email); + }, +); + +form.controls.email.setValue('user@example.com'); +form.controls.email.blur(); + +console.log(form.isValid); +console.log(form.getRawValue()); + +disposeReaction(); +form.dispose(); +``` + +## Public API + +Create forms with `createForm({ defaultValues, fields, arrays, validators })`. + +Field controls are available under `form.controls`: + +```ts +form.controls.email.value; +form.controls.email.displayValue; +form.controls.email.error; +form.controls.email.visibleError; +form.controls.email.isDirty; +form.controls.email.isTouched; +form.controls.email.isValidating; +form.controls.email.setValue('user@example.com'); +form.controls.email.blur(); +form.controls.email.reset(); +``` + +Form state is exposed through MobX-reactive getters: + +```ts +form.isValid; +form.isDirty; +form.isTouched; +form.isSubmitting; +form.errors; +form.getRawValue(); +await form.validate(); +await form.submit(); +form.reset(); +form.reset(nextValues); +form.dispose(); +``` + +Exports include `createForm`, `field`, `arrayField`, and public types for form +options, controls, arrays, validators, patches, and listeners. + +## Validation + +Field validators support sync, async, debounce, submit, blur, and linked-field +validation where TanStack Form Core supports the underlying behavior. + +```ts +const form = createForm({ + defaultValues: { + email: '', + confirmEmail: '', + }, + fields: { + email: field({ + validators: { + onChange: ({ value }) => + value.includes('@') ? undefined : 'Invalid email', + onChangeAsync: async ({ value, signal }) => { + await checkEmailAvailability(value, { signal }); + }, + onChangeAsyncDebounceMs: 300, + }, + }), + confirmEmail: field({ + validators: { + onChangeListenTo: ['email'], + onChange: ({ value, values }) => + value === values.email ? undefined : 'Emails must match', + }, + }), + }, +}); +``` + +Form-level validators can return a form error string or field errors: + +```ts +declare function checkEmailAvailability( + value: string, + options: { signal: AbortSignal }, +): Promise; + +const form = createForm({ + defaultValues: { + password: '', + }, + fields: { + password: field(), + }, + validators: { + onSubmit: ({ value }) => ({ + fields: { + password: value.password ? undefined : 'Required', + }, + }), + }, +}); +``` + +## Server Errors + +`applyServerErrors()` accepts dot/bracket paths and exposes simple string errors +on controls. + +```ts +form.applyServerErrors({ + email: 'Email already exists', + 'members[0].name': 'Required', +}); + +form.controls.email.error; +form.controls.email.visibleError; +``` + +Server errors are explicit external errors. They survive validation cycles, are +visible immediately, clear when the relevant field value changes, and clear on +`reset()`. + +## Arrays + +Use `arrayField({ getItemId })` for public array identity. `getItemId` must +return a unique stable id for every item; public identity never relies on array +indexes. + +```ts +import { arrayField, createForm, field } from '@revisium/forms-core'; + +const form = createForm({ + defaultValues: { + members: [{ id: '1', name: '' }], + }, + fields: {}, + arrays: { + members: arrayField<{ id: string; name: string }>({ + getItemId: (item) => item.id, + }), + }, +}); + +form.arrays.members.items; +form.arrays.members.push({ id: '2', name: 'Ada' }); +form.arrays.members.insert(1, { id: '3', name: 'Grace' }); +form.arrays.members.move(0, 1); +form.arrays.members.removeById('2'); +form.arrays.members.clear(); +``` + +Array items expose the current index, current value, stable id, and generated +controls for object item fields: + +```ts +const first = form.arrays.members.items[0]; + +first?.id; +first?.index; +first?.value; +first?.controls.name.setValue('Ada'); +``` + +## Autosave Patches + +`onPatch(listener)` emits value patches by diffing previous and current form +values. This is intended for autosave and persistence adapters. + +```ts +import type { FormPatch } from '@revisium/forms-core'; + +declare function queueAutosave(patch: FormPatch): void; + +const disposePatchListener = form.onPatch((patches) => { + for (const patch of patches) { + queueAutosave(patch); + } +}); + +form.controls.email.setValue('user@example.com'); + +disposePatchListener(); +``` + +Patch shape: + +```ts +type FormPatch = + | { type: 'set'; path: string; value: unknown; previousValue: unknown } + | { type: 'remove'; path: string; previousValue: unknown } + | { type: 'insert'; path: string; index: number; value: unknown } + | { type: 'move'; path: string; fromIndex: number; toIndex: number } + | { type: 'clear'; path: string; previousValue: unknown[] }; +``` + +Scalar and nested edits emit full paths such as `email` or `profile.name`. +Configured arrays emit stable operation patches for `push`, `insert`, +`removeAt`, `removeById`, `move`, and `clear`. Ambiguous bulk array changes fall +back to a `set` patch for the array path. + +## React Usage + +React integration is consumer-side. Create and own the form in your application +state layer, view model, or dependency-injection scope, then observe the public +MobX getters from React through your chosen MobX React binding. + +This package intentionally does not import React or export React components. + +## Why Not @tanstack/react-form + +`@tanstack/react-form` is a React adapter. `forms-core` needs a headless MobX +adapter that can live outside component lifecycle and be consumed by MobX +`computed`, `reaction`, and `autorun`. Using the React adapter as the main API +would make React a hidden runtime dependency and would not satisfy the +MobX-native contract. + +## Limitations + +Known limitations and deliberate constraints are tracked in +[docs/limitations.md](./docs/limitations.md). ## Development -Use Node.js 20.16 or newer. The CI target currently uses Node.js 24.11.1. +Use Node.js 24.11.1 or newer. CI runs the primary Sonar-enabled job on 24.11.1. ```bash npm ci npm run verify ``` -Useful local commands: +Useful commands: - `npm run tsc` - TypeScript typecheck. - `npm run lint:ci` - ESLint with zero warnings. - `npm run test:cov` - Jest coverage output for Sonar. -- `npm run sonar:local` - run Sonar locally from existing coverage. -- `npm run sonar:issues:local` - fail on unresolved Sonar issues. -- `npm run ci:local:sonar` - run local verify and then Sonar quality gate. - `npm run build` - package build and declaration output. -- `npm run markdown:lint` - Markdown and agent-rule lint. -- `npm run skills:lint` - repo-local agent skill structure lint. - -For local Sonar, either export `SONAR_TOKEN` in the shell or create an -untracked `.env.sonar` from `.env.sonar.example`. Do not commit real Sonar -tokens. Prefer running local Sonar after the branch has a GitHub PR: the script -auto-detects the PR and runs PR analysis. Before a PR exists, Sonar falls back -to branch analysis, which may be blocked by the organization plan. +- `npm run ci:local:sonar` - run local verify, Sonar quality gate, and issue + inspection. +- `npm pack --dry-run` - inspect package contents before publish. For PRs, do not treat Sonar `PASSED` as complete by itself. Inspect unresolved issues and fix every valid issue; this repo uses zero tolerance for PR Sonar issues. Pushes to `master` upload Sonar analysis but do not enforce total branch issue count. -## Bootstrap Pattern +## Release -This repo intentionally starts with docs and local agent workflows before source -code. See -[docs/handoff/bootstrap-new-library-project.md](./docs/handoff/bootstrap-new-library-project.md) -for the reusable pattern. +Release and npm publish rules are documented in +[docs/release-train.md](./docs/release-train.md). Publishing requires explicit +approval and uses shared workflows from `revisium/revisium-actions`. diff --git a/REVIEW.md b/REVIEW.md index de74262..4b4983f 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -14,11 +14,10 @@ Before reviewing a PR, read: 1. [docs/architecture.md](./docs/architecture.md) 2. [docs/review/forms-core-checklist.md](./docs/review/forms-core-checklist.md) -3. [docs/steps.md](./docs/steps.md) while implementation is in progress -4. [docs/pr-plan.md](./docs/pr-plan.md) -5. [docs/quality-gates.md](./docs/quality-gates.md) -6. [docs/readme-guidelines.md](./docs/readme-guidelines.md) for README changes -7. [docs/release-train.md](./docs/release-train.md) for publish or version work +3. [docs/pr-plan.md](./docs/pr-plan.md) +4. [docs/quality-gates.md](./docs/quality-gates.md) +5. [docs/readme-guidelines.md](./docs/readme-guidelines.md) for README changes +6. [docs/release-train.md](./docs/release-train.md) for publish or version work ## Source Of Truth diff --git a/docs/README.md b/docs/README.md index 8b86f1a..edecdfc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,25 +1,23 @@ # forms-core Docs -This directory is the implementation source of truth while the package is being -built. +This directory is the package source of truth for architecture, quality gates, +release workflow, review policy, and reusable handoff guidance. ## Reading Order 1. [Architecture](./architecture.md) -2. [Implementation Steps](./steps.md) -3. [PR Plan](./pr-plan.md) -4. [Quality Gates](./quality-gates.md) -5. [README Guidelines](./readme-guidelines.md) -6. [Known Limitations](./limitations.md) -7. [Release Train](./release-train.md) -8. [Review Checklist](./review/forms-core-checklist.md) -9. [Handoff](./handoff/README.md) +2. [PR Plan](./pr-plan.md) +3. [Quality Gates](./quality-gates.md) +4. [README Guidelines](./readme-guidelines.md) +5. [Known Limitations](./limitations.md) +6. [Release Train](./release-train.md) +7. [Review Checklist](./review/forms-core-checklist.md) +8. [Handoff](./handoff/README.md) ## Source-Of-Truth Rules - Public API and behavior belong in `architecture.md`. -- In-progress sequencing belongs in `steps.md`. -- PR boundaries belong in `pr-plan.md`. +- PR history and future maintenance boundaries belong in `pr-plan.md`. - Validation, CI, Sonar, and package checks belong in `quality-gates.md`. - Public package README shape belongs in `readme-guidelines.md`. - Release and npm publish rules belong in `release-train.md`. diff --git a/docs/handoff/README.md b/docs/handoff/README.md index d80a8dc..8bb54f6 100644 --- a/docs/handoff/README.md +++ b/docs/handoff/README.md @@ -7,44 +7,34 @@ This is the starting point for an agent or developer taking over the repository. 1. [AGENTS.md](../../AGENTS.md) 2. [REVIEW.md](../../REVIEW.md) 3. [docs/architecture.md](../architecture.md) -4. [docs/steps.md](../steps.md) -5. [docs/pr-plan.md](../pr-plan.md) -6. [docs/quality-gates.md](../quality-gates.md) -7. [docs/readme-guidelines.md](../readme-guidelines.md) -8. [docs/review/forms-core-checklist.md](../review/forms-core-checklist.md) -9. [docs/limitations.md](../limitations.md) -10. [docs/release-train.md](../release-train.md) +4. [docs/pr-plan.md](../pr-plan.md) +5. [docs/quality-gates.md](../quality-gates.md) +6. [docs/readme-guidelines.md](../readme-guidelines.md) +7. [docs/review/forms-core-checklist.md](../review/forms-core-checklist.md) +8. [docs/limitations.md](../limitations.md) +9. [docs/release-train.md](../release-train.md) ## Normal Iteration 1. Check branch and worktree. -2. Read the current unchecked item in [docs/steps.md](../steps.md). -3. Confirm the PR scope from [docs/pr-plan.md](../pr-plan.md). -4. Implement only that scope. -5. Add or update tests for real behavior. -6. Run required validation. -7. Update [docs/steps.md](../steps.md). -8. Run self-review against [REVIEW.md](../../REVIEW.md). -9. Commit, push, or open a PR only when the user asks. +2. Confirm the intended scope from [docs/pr-plan.md](../pr-plan.md) or the + current user request. +3. Update durable docs when behavior, public API, release workflow, or review + policy changes. +4. Add or update tests for real behavior. +5. Run required validation. +6. Run self-review against [REVIEW.md](../../REVIEW.md). +7. Commit, push, or open a PR only when the user asks. ## Repo-Local Skills Use skills in this order: -1. [`forms-core-implementation-step`](../../.agents/skills/forms-core-implementation-step/SKILL.md) - for normal implementation work. -2. [`forms-core-general-checks`](../../.agents/skills/forms-core-general-checks/SKILL.md) +1. [`forms-core-general-checks`](../../.agents/skills/forms-core-general-checks/SKILL.md) before handoff or PR updates. -3. [`forms-core-self-review`](../../.agents/skills/forms-core-self-review/SKILL.md) +2. [`forms-core-self-review`](../../.agents/skills/forms-core-self-review/SKILL.md) before calling the work ready. -4. [`forms-core-pr-review-iteration`](../../.agents/skills/forms-core-pr-review-iteration/SKILL.md) +3. [`forms-core-pr-review-iteration`](../../.agents/skills/forms-core-pr-review-iteration/SKILL.md) when GitHub review threads drive the work. -5. [`forms-core-pr-publish`](../../.agents/skills/forms-core-pr-publish/SKILL.md) +4. [`forms-core-pr-publish`](../../.agents/skills/forms-core-pr-publish/SKILL.md) only when the user asks to publish work. - -## Temporary Artifacts - -[docs/steps.md](../steps.md) is intentionally temporary. It coordinates the -multi-PR build. The final release-readiness PR should remove it or replace it -with durable history after the implementation is reflected in stable docs, -tests, and README examples. diff --git a/docs/pr-plan.md b/docs/pr-plan.md index b81b3db..ba362e9 100644 --- a/docs/pr-plan.md +++ b/docs/pr-plan.md @@ -1,7 +1,8 @@ # forms-core PR Plan -The repository should be built in small PRs. Each PR must leave the branch in a -reviewable state and update [docs/steps.md](./steps.md). +The repository was built in small PRs. Future maintenance PRs should keep the +branch reviewable and update durable source-of-truth docs when behavior, +release workflow, or review policy changes. ## PR 1 - Docs And Agent Contract diff --git a/docs/release-train.md b/docs/release-train.md index 5b3d70b..4fdcb2d 100644 --- a/docs/release-train.md +++ b/docs/release-train.md @@ -37,7 +37,8 @@ After `1.0.0`, use normal SemVer: - [ ] `npm pack --dry-run` shows only expected files. - [ ] README examples compile or are covered by tests. - [ ] `docs/limitations.md` is current. -- [ ] `docs/steps.md` has been removed or replaced with durable history. +- [ ] Temporary implementation trackers have been removed or replaced with + durable docs. - [ ] package exports are reviewed. - [ ] no React/UI dependency is present. - [ ] `@tanstack/form-core` and `mobx` dependency ranges are intentional. diff --git a/docs/steps.md b/docs/steps.md deleted file mode 100644 index 3f01f44..0000000 --- a/docs/steps.md +++ /dev/null @@ -1,168 +0,0 @@ -# forms-core Implementation Steps - -This is the temporary implementation tracker. Each agent must update it in the -same PR that completes or changes a step. - -Final cleanup rule: after the library is implemented, tested, documented, and -ready for release, remove this file or replace it with a short implementation -history. Durable facts must live in `README.md`, `docs/architecture.md`, -`docs/review/forms-core-checklist.md`, tests, and source comments where needed. - -## Status Legend - -- `[ ]` not started -- `[~]` in progress -- `[x]` complete and validated - -## PR 1 - Agent Contract And Architecture Docs - -- [x] Create `AGENTS.md`. -- [x] Create `CLAUDE.md -> AGENTS.md` symlink. -- [x] Create `.agents` skills and rules. -- [x] Create `.claude/skills -> ../.agents/skills` symlink. -- [x] Create `REVIEW.md`. -- [x] Create architecture, review, handoff, PR plan, release train, and - bootstrap-pattern docs. -- [x] Create README guidelines and quality-gate docs. -- [x] Add repo-local `scripts/lint-skills.mjs`. -- [x] Validate docs-only change with `git diff --check`. - -Exit criteria: - -- no package implementation; -- no dependencies added; -- future agents can start from `AGENTS.md` and `docs/handoff/README.md`. - -## PR 2 - Package Scaffold - -- [x] Add `package.json` for `@revisium/forms-core`. -- [x] Add TypeScript config. -- [x] Add build config. -- [x] Add test runner config. -- [x] Add lint and format config. -- [x] Add package entrypoint with placeholder exports only if needed. -- [x] Add `npm run verify` that runs typecheck, lint, test, build, and docs or - skill validation. -- [x] Wire existing local skill lint into `npm run skills:lint`. -- [x] Add Markdown lint and Prettier check for docs. -- [x] Add coverage output suitable for Sonar. -- [x] Add initial CI workflow once `npm run verify` exists. -- [x] Add Sonar scan wiring and project configuration. -- [x] Add local Sonar scripts and bootstrap docs. -- [x] Add zero-tolerance Sonar issue inspection. -- [x] Add package publish metadata without publishing. - -Allowed dependencies: - -- runtime: `@tanstack/form-core`, `mobx`; -- test/dev dependencies only when needed for local scripts. - -Exit criteria: - -- `npm run verify` passes locally and in CI; -- package can build without implementation shortcuts; -- no React dependencies. - -## PR 3 - Private MobX Selector Bridge - -- [x] Implement internal bridge over `{ get, subscribe }`. -- [x] Use `observable.box(..., { deep: false })`. -- [x] Update selected value inside `runInAction`. -- [x] Support custom equality comparator. -- [x] Support unsubscribe object and unsubscribe function shapes. -- [x] Expose `value` and `dispose()` internally. -- [x] Keep bridge out of public exports. -- [x] Add unit tests for reaction updates and disposal unsubscribe. - -Exit criteria: - -- MobX `reaction` fires when selected TanStack-like state changes; -- no consumer-facing subscription API. - -## PR 4 - Form And Control Wrappers - -- [x] Implement `createForm`. -- [x] Implement `field`. -- [x] Own `FormApi` and mount/dispose lifecycle. -- [x] Expose form getters: `isValid`, `isDirty`, `isTouched`, `isSubmitting`, - `errors`. -- [x] Expose form commands: `getRawValue`, `reset`, `submit`, `validate`, - `dispose`. -- [x] Expose control getters: `value`, `displayValue`, `error`, `visibleError`, - `isDirty`, `isTouched`, `isValidating`. -- [x] Expose control commands: `setValue`, `blur`, `reset`. -- [x] Normalize TanStack error maps. -- [x] Add tests for scalar value reactivity, validity reactivity, reset, dirty, - touched, and no manual subscribe usage. - -Exit criteria: - -- target scalar public API works; -- React is not imported. - -## PR 5 - Validation And Server Errors - -- [x] Support sync field validators. -- [x] Support async field validators. -- [x] Support async debounce. -- [x] Support linked/dependent field validation where TanStack supports it. -- [x] Support form-level validation. -- [x] Support submit validation. -- [x] Implement `applyServerErrors`. -- [x] Clear server errors on relevant value changes and reset. -- [x] Add tests for validation, debounce, linked validation, form validation, - submit validation, server error visibility, and server error clearing. - -Exit criteria: - -- server-error lifecycle is explicit and tested; -- no parallel validation engine. - -## PR 6 - Nested Paths And Arrays - -- [x] Add nested object path helpers. -- [x] Support nested control paths. -- [x] Implement `arrayField`. -- [x] Implement array item model with `{ id, index, controls, value }`. -- [x] Implement `push`, `insert`, `removeById`, `removeAt`, `move`, `swap`, - `clear`. -- [x] Preserve stable public ids through index changes. -- [x] Add tests for nested object path and stable array item ids. - -Exit criteria: - -- public item identity never uses array index; -- nested paths work in values, errors, and patches where applicable. - -## PR 7 - Patches, Disposal, And Hardening - -- [x] Implement `onPatch`. -- [x] Diff scalar, nested object, and array changes. -- [x] Emit useful patches for autosave. -- [x] Dispose patch listeners. -- [x] Harden disposed-object behavior. -- [x] Add tests for scalar patches, nested patches, array patches, listener - disposal, bridge disposal, and form disposal. - -Exit criteria: - -- autosave listener can consume emitted patches without reading raw TanStack - state. - -## PR 8 - README, API Polish, And Release Readiness - -- [ ] Replace placeholder README with real usage examples. -- [ ] Bring README to the structure in `docs/readme-guidelines.md`. -- [ ] Document known limitations. -- [ ] Document why `@tanstack/react-form` is intentionally not used. -- [ ] Confirm public exports. -- [ ] Run full verify. -- [ ] Run `npm pack` and inspect package contents. -- [ ] Add release train and npm publish workflows using `revisium-actions`. -- [ ] Inspect CI and Sonar/quality gate status when configured. -- [ ] Remove or replace this temporary `docs/steps.md`. - -Exit criteria: - -- package is ready for an npm publish PR or release workflow; -- no temporary implementation-only artifacts remain. diff --git a/package-lock.json b/package-lock.json index 8b2d583..92feb0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "typescript-eslint": "^8.15.0" }, "engines": { - "node": ">=20.16.0" + "node": ">=24.11.1" }, "peerDependencies": { "mobx": "^6.0.0" diff --git a/package.json b/package.json index f7e11fd..35b4c8e 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "typescript-eslint": "^8.15.0" }, "engines": { - "node": ">=20.16.0" + "node": ">=24.11.1" }, "publishConfig": { "access": "public"