Files
2026-05-27 23:29:53 +01:00

9.2 KiB

Tasks — enterprise-rules-migration

Every checkbox MUST reflect work actually done in this change. Retroactive ticking is the failure mode this template guards against.

1. Spec authoring

  • 1.1 Read ../suaveplan/enterprise/rules.md end-to-end
  • 1.2 No prior openspec/specs/rules/spec.md exists — new capability
  • 1.3 Read openspec/AGENTS.md Sections 13 and 14
  • 1.4 Draft proposal.md from templates/proposal.md
  • 1.5 Source mapping table cites enterprise/rules.md lines for every requirement
  • 1.6 Draft specs/rules/spec.md and specs/rules-hono/spec.md
  • 1.7 Every requirement carries a **Verified by:** line

2. Implementation — @suaveplan/rules

2.1 Package scaffold — ✅ landed 2026-05-11

  • 2.1.1 packages/workflow/rules/package.json (catalog deps + workspace @suaveplan/error, events, types)
  • 2.1.2 tsconfig.json + tsconfig.lib.json extend tsconfig.base.json
  • 2.1.3 vite.config.ts (preserveModules, dts)
  • 2.1.4 vitest.config.ts (istanbul, 100/100/100/100 thresholds)
  • 2.1.5 Subpath exports declared for every feature module (engine, builder, context, actions, events, operators, validators/{schema,field,custom}, serializer, optimizer, types, errors)

2.2 Types + errors + constants — ✅ landed 2026-05-11

  • 2.2.1 src/types/ — rule.types, context.types, operator.types, action.types, event.types, validator.types, result.types, guards.ts, index.ts
  • 2.2.2 src/errors/rules-error.ts — defineCodes("RULES", [24 codes]) + RulesError extends SuaveplanError<RulesErrorCode> + isRulesError guard + sibling .test.ts + sibling .md
  • 2.2.3 src/constants.ts — PACKAGE_NAME, VERSION, DEFAULT_MAX_DEPTH, DEFAULT_EVENT_HISTORY, DEFAULT_ACTION_TIMEOUT_MS, DEFAULT_ACTION_PARALLELISM + sibling .md

2.3 Engine + builder + context — ✅ landed 2026-05-11

  • 2.3.1 src/engine/engine.ts — RuleEngine with evaluate/evaluateAll/evaluateRuleset/check, depth limit (RULES.ENGINE_DEPTH_EXCEEDED), plugin registration (idempotent), AbortSignal (RULES.ENGINE_ABORTED), OTel histogram + counters + span; internal compareValues dispatcher over 33 operators + sibling .md + sibling test
  • 2.3.2 src/builder/builder.ts — RuleBuilder (fluent + pending-operator combine), FieldBuilder (30 operator methods), rule/condition/and/or/not/group factories + sibling .md + sibling test
  • 2.3.3 src/context/context.ts — ContextBuilder (immutable per call), DefaultContextProvider (injectable Clock, enhancer pipeline), DefaultFieldResolver (dot-path + prop[N], strict mode → RULES.FIELD_RESOLUTION_FAILED), createRequestContext/createScopedContext, getField/hasField, Zod-validated options + sibling .md + sibling test
  • 2.3.4 src/otel/otel.ts — lazy OTel instruments (getInstruments / resetInstruments) + sibling .md

2.4 Actions + events

  • 2.4.1 src/actions/actions.ts — Action, ActionRegistry, ActionExecutor, createAction/createActionRegistry/createActionExecutor, validateActionConfig, builtInHandlers (log/set/delay/noop with LoggerLike)
  • 2.4.2 src/events/event-bus.ts — EventBus, HookManager, bounded history, priority hooks, waitFor with timeout + AbortSignal

2.5 Operators

  • 2.5.1 src/operators/registry.ts — OperatorRegistry
  • 2.5.2 src/operators/defaults.ts — 30 built-in operators + defaultOperatorRegistry registration

2.6 Validators

  • 2.6.1 src/validators/schema/schema.ts — SchemaValidator with formats, transforms, recursion, AbortSignal
  • 2.6.2 src/validators/field/field.ts — FieldValidator, field(), 12 preset chains
  • 2.6.3 src/validators/custom/custom.ts — CustomValidator, ValidatorRegistry, 17 built-ins + composeValidators + defaultValidatorRegistry

2.7 Serializer + optimizer

  • 2.7.1 src/serializer/serializer.ts — full serialize/deserialize surface with function-placeholder strategy + Zod boundary
  • 2.7.2 src/optimizer/optimizer.ts — optimizeRule/analyzeRule/estimateCost/findFields/hasAsyncComponents/countRules

2.8 Barrel + benchmarks

  • 2.8.1 src/index.ts — explicit named re-exports for every public symbol (no export *)
  • 2.8.2 src/__benchmarks__/ — mitata benchmarks for engine.evaluate + serializer round-trip + optimizer

3. Implementation — @suaveplan/rules-hono

  • 3.1 packages/workflow/rules-hono/package.json (peer hono, dep @suaveplan/rules)
  • 3.2 tsconfig + vite + vitest configs
  • 3.3 src/middleware/rule.ts, validation.ts, access-control.ts, response-validation.ts, rate-limit-rule.ts, combine.ts
  • 3.4 src/engine/default.ts — lazy getDefaultEngine + resetDefaultEngine under ./testing subpath
  • 3.5 OTel surface for every middleware
  • 3.6 Barrel + subpath exports

4. Tests

  • 4.1 Unit tests per feature module covering every scenario (every it() has { timeout: 5000 })
  • 4.2 Spec-conformance tests (*.spec-conformance.test.ts) pinning type-level / OTel / RulesError-code-thrown / strict-mode scenarios
  • 4.3 Property-based tests on operators (commutative/associative) + optimizer + serializer round-trips (fast-check)
  • 4.4 Hono middleware tests via in-process app.request(...) (no mocks)

5. Documentation

  • 5.1 Sibling .md ≥200 words for every implementation .ts (Purpose / Features / Basic / Advanced / API Reference / Implementation Notes)
  • 5.2 Root README.md per package with Modules section linking every co-located .md
  • 5.3 Update root CLAUDE.md package listing
  • 5.4 Update STATUS.md (rules row + new rules-hono row)
  • 5.5 Update SUMMARY.md workflow section

6. 5-gate verification

  • 6.1 bun run check — 0/0/0
  • 6.2 bunx turbo lint typecheck test --filter=@suaveplan/rules — green
  • 6.3 bunx turbo lint typecheck test --filter=@suaveplan/rules-hono — green
  • 6.4 bunx turbo test:coverage --filter=@suaveplan/rules — 100/100/100/100
  • 6.5 bunx turbo test:coverage --filter=@suaveplan/rules-hono — 100/100/100/100
  • 6.6 bun scripts/openspec-conformance.ts @suaveplan/rules — exit 0
  • 6.7 bun scripts/openspec-conformance.ts @suaveplan/rules-hono — exit 0
  • 6.8 bunx turbo build --filter=@suaveplan/{rules,rules-hono} — green

7. Pre-archive 9-row checklist

  • 7.1 Generics threaded; expectTypeOf assertions on every literal-type claim (type guards)
  • 7.2 Zod schemas wired at every declared boundary (RuleSchema, RulesetSchema, SerializedRuleSchema, SchemaSchema, ContextSchema, MiddlewareOptionsSchemas)
  • 7.3 Every code in defineCodes("RULES", [...]) thrown by ≥1 production path
  • 7.4 Strict-mode behaviors tested both on and off (FieldResolver strict, abortEarly)
  • 7.5 OTel instruments emitted from production paths, asserted with in-memory MeterProvider
  • 7.6 Lifecycle dedup tested (engine.use(plugin) idempotent, getDefaultEngine returns identical reference)
  • 7.7 No retroactive tick — every box reflects work done in this change
  • 7.8 No export * in feature-module barrels (§6a) — grep clean
  • 7.9 No direct vitest / bun:test imports — grep clean

Continuation pickup point (next session)

State as of 2026-05-11 ~15:55:

Sections 1, 2.1, 2.2 are complete. Section 2.3 (engine + builder + context) is the next atomic unit — start there.

Recommended order:

  1. 2.3 engine + builder + context — the engine consumes context + operators; builder produces rules. Implement context first (FieldResolver with dot-path/array-index, ContextBuilder, DefaultContextProvider, createRequestContext with injectable Clock), then builder, then engine (depth limit, AbortSignal, OTel histograms, plugin registration).
  2. 2.5 operators — 30 built-ins are independent of engine internals beyond the OperatorDefinition shape (already in types). Can be implemented in parallel with 2.3 via subagent.
  3. 2.4 actions + events — depend on context + operators. EventBus consumes injectable Clock.
  4. 2.6 validators — three modules (schema/field/custom), each independent.
  5. 2.7 serializer + optimizer — depend on the full Rule surface being stable.
  6. 2.8 barrel + benchmarks — finalise src/index.ts to re-export every public symbol (no export *).
  7. 3.x rules-hono — only after the rules core is green at 100% coverage.
  8. 5.x docs + 6.x gates + 7.x checklist + 8.x archive — final sequence.

Verification stub to land mid-stream once 2.3 is in:

cd /home/paul/web/genesis && bun install
bunx turbo lint typecheck test --filter=@suaveplan/rules
bunx turbo test:coverage --filter=@suaveplan/rules

Coverage must already be 100/100/100/100 on the foundation (types + errors + constants) before adding 2.3 on top.

8. Archive

  • 8.1 Fold specs/rules/spec.md into openspec/specs/rules/spec.md
  • 8.2 Fold specs/rules-hono/spec.md into openspec/specs/rules-hono/spec.md
  • 8.3 Move openspec/changes/enterprise-rules-migration/ → openspec/archive/<yyyy-mm-dd>-enterprise-rules-migration/
  • 8.4 bun scripts/folder-reorg/generate-manifest.ts
  • 8.5 bun scripts/generate-missing.ts
  • 8.6 bunx markdownlint-cli2 --fix