Companion to REGRESSION_LOG.md (test batches + bugs found). Loose rules: drop things in freely, re-sort when you revisit. Link to code paths the same way REGRESSION_LOG does so the list doubles as a jump-table.
ChatEditFlow in Flutter (message list + AbortController-style
cancellation + <spec>...</spec> proposal protocol + per-card
Apply / Discard). Reuses existing ai_provider.dart +
ai_edit_prompt.dart; needs a sibling ai_chat_prompt.dart
(mirror of ai-chat-prompt.ts)
and a stateful chat screen. Closes the last big parity gap from
ADR-0003 §5 (“Flutter feature parity for chat mode”).CHANGELOG.md — low value until releases start; relevant
once the conformance suite pins spec versions.recordSource default-order parity — surfaced by s23
(record navigation). PocketBase defaults to created desc, so
FakeDataService.query reverses results to mimic that; SQLite’s
default scan returns insertion order. Same spec → different
“first record” across frameworks. The s23 scenario currently
sidesteps this with order-agnostic assertions. Real fix: add an
optional sort directive to the firstRecord action (and/or
pick a single canonical default) so firstRecord is
deterministic for a given spec on every renderer.clickTab(label) primitive + test that switching tabs
changes which is active, and that the snapshot only exposes
the active tab’s content (if that’s the actual contract).
- s18 chart: only asserts config propagation; no
aggregation math. Add a variant that seeds rows, snapshots
the chart’s series values (would need seriesCount /
seriesData in the snapshot, not just a hardcoded 1).conformance/scenarios.json (just the metadata;
run bodies stay per-language); add a meta-test on each side
that asserts “every scenario id in the catalog has a scenario
implementation.” Prevents the two sides from silently drifting
in which scenarios they advertise.code_generator.dart
still uses getApplicationDocumentsDirectory() directly in its
emitted code (see code_generator.dart:475).
Low priority because the generated app runs on someone else’s
machine, but worth revisiting for consistency.settings.json on
Flutter). Move to OS-keychain (keytar-equivalent on web is a
non-starter, so React likely stays in localStorage or moves
to a server proxy; Flutter has flutter_secure_storage). Wants
its own ADR — different APIs per platform, different web/desktop
story, masking-on-display already in place either way.estimateCost exists on
both providers
(ai-provider.ts /
ai_provider.dart)
and is unit-tested, but neither screen surfaces the estimate
before firing the request. Add a configurable threshold (default
~$0.10) — over it, show “this request is estimated at $X, send
anyway?” before the call. Open question per ADR-0003 §5.seedData on a data source
can carry real names / emails. Currently we send the spec
verbatim. Add an opt-in toggle in AI Settings that runs a
lightweight redactor over seedData[] arrays before the prompt
is built (replace strings matching email / phone shapes with
placeholders). Default off; surface the disclosure inline in the
AI Settings panel either way. Open question per ADR-0003 §5.Frameworks/react-web/stryker.config.json, weekly
.github/workflows/mutation.yml,
npm run mutation). Mirror with mutation_test package on the
Flutter side once the React baseline stabilises and we know
whether it’s catching real gaps.@axe-core/playwright is already a
devDep, tests/e2e/accessibility/
is empty. A pass on key flows (login, dashboard, app home) would
catch a lot cheaply.ods-conformance and let 3rd-party frameworks
self-certify. Ecosystem play.AnthropicProvider and OpenAiProvider. ADR-0003 §5
open question; gated on whether users actually complain about
latency.addField, renameDataSource, etc.). Cuts output tokens on
large specs and gives us a typed surface to validate before
apply. Cost: provider-specific tool schemas (Anthropic + OpenAI
diverge here), more round-trips, the proposal protocol gets
more complex than <spec>...</spec>. ADR-0003 §5 open question.AnthropicProvider (Messages API) +
OpenAiProvider (Chat Completions). Pure HTTP, transport-injected,
38 unit tests + 8 properties (~240 random inputs).localStorage (ods_ai_settings); Flutter stores in
SettingsStore. jsdom localStorage shim added to vitest setup
(the test environment was missing removeItem).EditWithAiScreen falls back to copy/paste when no key is
configured. 12 prompt-builder tests cover
ai-edit-prompt.ts.<spec>...</spec> proposal protocol; ChatEditFlow
renders message bubbles + per-card Apply / Discard +
AbortController-style Stop. Fixed a “Failed to execute ‘fetch’ on
‘Window’: Illegal invocation” regression by binding the default
fetch impl to globalThis (regression test pinned).ai:provider capability + AiRequestSnapshot +
simulateAiRequest driver method on the OdsDriver contract;
both drivers spin up the named provider against a fake transport
and return the captured wire shape; s27 pins both endpoint
URLs, both auth-header forms (x-api-key: vs authorization:
Bearer), and the per-provider messages-array shape._EditWithAiScreen branches on SettingsStore.isAiConfigured
(one-shot mode shows instruction → Generate → side-by-side
before/after → Apply / Regenerate / Discard with spec
validation; copy/paste stays as the no-key fallback).
ai_edit_prompt.dart
mirrors React’s helpers (12 unit tests).Frameworks/conformance/** (was a hidden path-filter gap);
publish.sh adds npx tsc -b + flutter analyze so the
static checks CI runs also fail locally. Conformance count
bumped to 26 scenarios × 2 drivers = 52 parity tests.models 90%, parser 90%, engine 50%) plus a
Dart tool/coverage_check.dart that parses lcov.info and
enforces lib/engine 60% / lib/models 85% / lib/parser
80%. Both fail-non-zero on regression.buildUpdatedSpecJson on both
frameworks
(theme-spec-writer.ts
/ theme_spec_writer.dart)
replaces inline JSON munging in the admin save-to-spec path.
28 unit tests added (14 per framework).pumpAndSettle waits forever. Fix: bootEngineFor /
disposeAllFor wrap setup in tester.runAsync to escape the
FakeAsync zone; pumpAndSettle does fixed real-time pump
rounds (16 × 100ms) instead. Plus a pumpUntilFound helper
for cases where fixed timing isn’t enough.
_test_harness.dart.SingleChildScrollView
around a ListView (nested viewports), assertions for column
headers / search inputs that the renderer hides under the
“No data yet” empty state, a form referencing a formId not
in the spec’s pages.npm run mutation, .gitignore updates, weekly
.github/workflows/mutation.yml
+ workflow_dispatch. Threshold gate is break: 0 for now —
switch to a real break threshold after 2-3 weekly runs settle.OdsBranding entirely; replaced with
OdsTheme { base, mode, headerStyle, overrides }. Logo /
favicon / appIcon lifted to top-level OdsApp. Fonts moved
onto the theme. Both parsers + 17 in-repo specs +
ods-schema.json rewritten to the new shape — no parser shim.<link> tags). UI label moved to “App identity
& typography”.currentAppId + rawSpecJson plumbed through the
app store; non-admins continue to write per-app localStorage.themeConfig() driver method + ThemeConfig
type added to both contracts; conformance scenario s21 verifies
both parsers produce the same {base, mode, headerStyle,
overrides, logo, favicon} shape from the same spec.AppEngine.loadedAppId + rawSpecJson + hotReplaceSpec,
LoadedAppsStore.findById, dual-write in
settings_screen.dart
to update the spec via LoadedAppsStore.updateApp and
hot-replace the in-memory model so the UI reflects the change
immediately.publish.sh test gate, when to add a conformance
scenario, the test layers, and PR expectations. Refers to
ARCHITECTURE / CONVENTIONS / TROUBLESHOOTING rather than
duplicating.Specification/CODE_OF_CONDUCT.md to root; removed the stale
Specification/CONTRIBUTING.md.DataStore.setupDataSources + React
DataService.setupDataSources to auto-append the ownership
column (ds.ownership.ownerField) to a data source’s fields
when ds.ownership.enabled is true, unless the builder
already declared it manually. Each has a small
_fieldsWithOwnership / fieldsWithOwnership helper mirroring
the other. Idempotent on re-runs because ensureTable /
ensureCollection handle the “column already exists” case
non-destructively._owner-in-fields workaround from
ownershipPerUser.json —
s16 now passes against the canonical spec shape (ownership
config + no explicit _owner field).parentField /
childDataSource / childLinkField directly (instead of
using cascadeMatchField as parentField, which broke when
matchField differed from the renamed field). Legacy nested
form kept as fallback.cascade.parentField over its withData-key
deduction when computing cascadeOldValue.cascadeRename capability declared on FlutterDriver; React
already had it. Conformance total: 19 → 20 scenarios. React
1145 → 1146; Flutter 808 → 809. Test-count bump also
reflects the batch-2 integration tests which now compile/run
against the updated cascade runtime.detail scenario,
deepening tabs (clickTab primitive) and chart (series
math) from initial-state to behavior, scenario-catalog
parity check. Each kept as a standalone Later item so the
Now/Next lists stay focused.chartType,
title, and dataSource config from spec. Narrow parity check
— no aggregation math verified. ChartSnapshot added to the
Dart contract; chart capability on FlutterDriver.dragCard(dataSource, rowId, toStatus)
on both OdsDriver contracts; both drivers implement it via the
PUT data source (or fall back to the kanban’s own dataSource).KanbanSnapshot + KanbanColumn added to Dart contract;
FlutterDriver emits it. kanban capability declared on
FlutterDriver.TabsSnapshot + TabsTab to the Dart contract;
FlutterDriver now emits a TabsSnapshot for OdsTabsComponent.tabs capability declared on FlutterDriver (already on React).dataRows (the
god view) sees all rows regardless of session.queryDataSource (React store) / engine.queryDataSource
(Flutter) — so rowCount reflects what the current user
would actually see, including ownership filtering. Previously
both used the raw unfiltered query.auth:ownership capability declared on FlutterDriver (was
already on React)._owner column for ownership-enabled data sources; the
insert then fails on SQLite’s strict schema. Workaround in
the scenario spec: declare _owner explicitly in fields.value resolves aggregate
expressions ({COUNT(tasks)}) against the data source; label
passes through from spec. Exercises the summary capability
on both drivers and the AggregateEvaluator /
resolveAggregates cross-framework equivalence.snapshotComponent summary branch —
previously read non-existent s.defaultValue and returned ‘’.
Now uses s.value and resolves aggregates via the real
AggregateEvaluator. Added SummarySnapshot to the Dart
contract + summary capability on FlutterDriver.DataService.setupDataSources so scenarios
can declare seedData on local data sources.handleCascade and
Flutter’s AppEngine.executeActions disagree on which cascade
shape is canonical. No spec satisfies both. Scenario deferred
until the bug is fixed; new Next-list item captures the work.{quantity} *
{unitPrice}) and text interpolation ({firstName}
{lastName}), plus the “any empty dependency → empty result”
guard both evaluators enforce.formValues using their
native evaluateFormula / FormulaEvaluator — the same
evaluator the form renderer uses, so driver output mirrors
what a user would see in the UI.formulas capability declared on both drivers. Conformance
total: 13 → 14 scenarios. React 1139 → 1140; Flutter 802 → 803.onEnd navigate — pins Bug #2
fix (universal onEnd). New
submitThenNavigate.json.action: update writes literal
values to the matched row. Forces both drivers to extend
clickRowAction beyond delete-only — the ReactDriver now
dispatches update via executeActions, the FlutterDriver
via engine.executeRowAction. New
rowActionUpdate.json.action:update capability declared on both drivers.
Conformance total: 11 → 13 scenarios. React 1137 → 1139;
Flutter 800 → 802.label vs header parity bug earlier today.scenarios.ts dropped 260+
lines, scenarios.dart dropped ~270 lines. Run bodies stay
per-language — the per-driver assertion code is what needs
to diverge.clickRowAction (delete), full visibleWhen evaluation (field
+ data), setClock via a driver-local _fakeNow that
formValues lazily consults for CURRENTDATE/NOW defaults,
and login / registerUser / currentUser / logout wired
through the real AuthService + SQLite _ods_users table._boot now only sets skipAppAuth=true for
single-user specs, so multi-user scenarios get proper
AuthService.initialize() — creating _ods_users +
_ods_user_roles tables.AppEngine + SQLite (temp-dir per
scenario). Flutter suite 789 → 794 tests.label on list columns; both React and Flutter parsers
actually read header — TS silently accepted undefined,
Flutter parser threw. Fixed specs to use header on both
sides. (b) Dart OdsAction was missing the level field
(TS had it after s03); added to the Dart model + fromJson.publish.sh gates now include
test/conformance. Parity drift between frameworks will
block merges.pb that
AuthService touches (authStore, collection('users') CRUD
+ authWithPassword). Replaces the old mockPb() in
conformance so the real AuthService runs against it —
no parallel auth implementation.login + registerUser wired through
AuthService (was throwing). Role defaults to the spec’s
auth.defaultRole when caller omits it.false, session
untouched; subsequent correct password succeeds.clickRowAction implemented — delegates to
executeDeleteRowAction on the store; resolves non-_id
matchFields by looking up the row first. Non-delete actions
still throw (MVP scope). Pinned by s06
(scenarios.ts).visibleWhen evaluation in snapshots — mirrors the React
renderer’s logic: field-based conditions read form state,
data-based conditions query the data service for row counts.
Pinned by s07 (field) + s08 (data count).setClock uses vi.setSystemTime + useFakeTimers; restored
on unmount. formValues now lazily resolves
CURRENTDATE/NOW defaults for unset fields using the current
clock. Pinned by s09.One-does-simply/one-does-simply.github.io repo with a
standalone index.html — now live at
https://one-does-simply.github.io/. Inherits the indigo/purple
brand palette from
Specification/index.html; lists
active (ods-pages) + planned (ods-chat, ods-workflow,
ods-game) families.blog field in the API) to the
new landing page. Old stale URL
(one-does-simply.github.io/Specification/) is now fully dead
everywhere: org URL, repo homepages, runtime code.ods-pages (main branch, / root);
URL base moves from
https://one-does-simply.github.io/Specification/...
(stale archived snapshot, can’t update) to
https://one-does-simply.github.io/ods-pages/Specification/...
(tracks main).publish.sh,
TODO/REGRESSION_LOG format, ADR convention, CI pattern,
CLAUDE.md) and what stays per-family. Calls the duplicate-
don’t-extract decision explicitly.One-does-simply/.github repo created with
profile/README.md. Visitors landing on
github.com/One-does-simply
now see an ODS family overview with active/planned siblings.LICENSE (MIT) — mirrors
Specification/LICENSE. Fixes “No
license” label visitors see in the GitHub sidebar.gh repo edit
(spec-driven, low-code, react, flutter, pocketbase,
typescript, dart, monorepo) — improves discoverability.publish.sh: runs
test/engine test/models test/parser test/integration
--exclude-tags=slow instead of the whole tree. Widget tests
were hanging on GH Linux runners (same root cause as the
local Windows skip).Specification, Frameworks, BuildHelpers)
archived on GitHub. Physical consolidation itself landed in
commit d5c165e (2026-04-20) as a single bulk-add rather than a
history-preserving git subtree add — original sub-tree
histories now live in the archived upstreams._token rename in
ColorCustomizer.tsx;
_app/_page mis-prefix in
code-generator.ts
(8 errors from one signature); SendOptions type on the
pb.send wrapper in
pocketbase.ts;
removed dead cardRotation helper + unused rotation memo in
KanbanComponent.tsx.npx tsc -b step to
.github/workflows/react.yml so
type regressions block CI..github/workflows/
(flutter.yml, react.yml).
The nested copies under Frameworks/*/.github/workflows/ were
orphaned after the monorepo consolidation (GitHub Actions only
reads workflows from repo-root .github/workflows/) — neither
framework had working CI. Adjusted paths for the new layout
(single checkout, no separate Specification clone).slow-tagged Batch 9 perf tests
(flutter test --exclude-tags=slow) so I/O-bound perf tests
don’t flake the gate on cold runners.tests/e2e/global-setup.ts).OdsAction parser preserves level on showMessage
(ods-action.ts);
conformance s03 tightened to assert level: 'success'; driver
dropped its unsafe cast
(react-driver.ts).slow via library-level @Tags(['slow'])
in batch9_performance_test.dart;
dart_test.yaml declares the tag; publish.sh runs with
--exclude-tags=slow.A couple of comments.txt TODO entry removed (file was
already gone).appPrefix isolation unit tests — pin the multi-app collection
contract at the real DataService boundary
(data-service.test.ts).LoginScreen signup-clears-both-gates regression test — renders
<LoginScreen> with needsAdminSetup=true, runs signup,
asserts both needsLogin and needsAdminSetup clear; failure
path leaves gates intact
(LoginScreen.test.tsx).pocketbase.ts module-init guard — source-level test fails if
pb.authStore.clear() creeps back into
pocketbase.ts
(pocketbase.test.ts).users collection auto-create
(AuthService.ensureUsersCollection)needsAdminSetup-on-signup latent bugdata-ods-kanban-* attrspb.authStore.clear() from pocketbase.ts
so admin sessions persist across navigation_InlineUserList + UserManagementScreenCURRENT_USER.EMAIL token in Flutter form resolver + currentEmail
on AuthService / FrameworkAuthServicec:\Apps\One-does-simply.claude/settings.json allowlist tuned (fewer permission prompts)