release: v1.2.0 — TIME columns, WEEK()/DATEADD(), BROWSE cell validation, Overtime demo#57
Merged
Conversation
Bump version to 1.2.0 and add Unreleased changelog heading for TIME columns, WEEK(), BROWSE cell validation, and the Overtime Tracking demo.
Add TIME/TIME(n) as a first-class CREATE TABLE column type, stored as HH:MM text. The optional TIME(n) qualifier requires minutes to be a multiple of n (e.g. TIME(15) for quarter-hour increments). A new ColumnMetaStore (data/system.sqlite3, same pattern as IndexStore) tracks base type + qualifier per column since SQLite's own type affinity can't distinguish TIME from CHAR/DATE (all TEXT). REPLACE ... WITH validates against it and rejects malformed or off-granularity values instead of silently coercing them; LIST STRUCTURE prints the declared type. The New table wizard and Modify structure wizard both offer TIME as a column type.
feat: TIME column type with granularity qualifier (#43)
WEEK(date) returns the ISO-8601 week number (1-53): Monday-start weeks, week 1 being the week that contains the year's first Thursday. Early-January dates therefore report the previous year's week 52/53, and late-December dates week 1 of the next year. Accepts ISO YYYY-MM-DD or MM/DD/YY. Computed in UTC so a local timezone offset can't shift the day, and ISO-shaped but impossible dates (2024-02-30, 2023-02-29) return 0 rather than silently rolling over the way Date.UTC does. Registered in Parser's BUILTIN_FUNCTIONS — implementing a built-in without whitelisting it there is how the #4 built-ins shipped broken. Covered directly (Builtins.test.ts), through the parser (BuiltinsParse.test.ts), and end-to-end in the REPL (parity-commands.spec.ts), asserting the printed value. Also refreshes the doc test counts and adds ColumnMetaStore.ts / TimeType.test.ts to the CLAUDE.md trees, both missed in #43.
feat: WEEK() ISO-8601 week-number built-in (#44)
Grid edits are now checked against the column's declared type before they commit. An invalid edit keeps the cell in edit mode, outlined in red, showing why; the error clears as soon as the value becomes valid, and Esc abandons the edit leaving the original value intact. The rules live in src/shared/cellValidation.ts and run on both sides: Grid.ts for instant feedback, and Session's grid-edit handler as the authoritative check. grid-edit previously bypassed the Executor and wrote straight to SQLite with no validation at all, so a WS message could plant any value in any column. Serving that needed the declared type of every column, not just TIME: SQLite's affinity cannot tell TIME/DATE/CHAR apart (all TEXT), nor LOGICAL from INT (both INTEGER), nor recover a NUM(p,s) qualifier. CREATE TABLE now records all of them, grid-open ships them to the client, and LIST STRUCTURE prints them as declared. Two bugs surfaced and are fixed here: - CREATE TABLE t (price NUM(8,2)) created a phantom column named "2" of type ")" — the parser read the precision and then treated the scale as the next column definition. The PRODUCTS, DEALS and SALES demo tables all carried the stray column. - Column metadata was keyed by (table, column) with no database scope, so same-named tables in different databases overwrote each other's declared types. Now keyed by (db, table, column), with a migration for the pre-#45 schema.
feat: BROWSE per-cell validation by declared column type (#45)
Two bugs shipped through a 283-test suite. Both were structural blind spots in how the tests were written, not bad luck: - Nearly every assertion is `toContain` on rendered text, which can prove a thing is present but never that something extra is absent. A phantom column named "2" therefore sailed through every LIST and LIST STRUCTURE check. - Four of twelve ClientMessage types had no test at all. grid-edit wrote straight to SQLite unvalidated because the grid tests only opened the grid and pressed Escape. Root cause first: parseCreate used to absorb any token it did not understand and invent a column from it. It now rejects a malformed column list — missing comma, missing type, unclosed paren, a third type argument — naming the offending column and creating nothing. Then the coverage those bugs needed: - DemoSchemas.test.ts pins the exact column list of every table the demos create, and asserts no column name is a bare number. - GridMessages.test.ts drives grid-edit, grid-delete, grid-new-row and grid-refresh, asserting the effect on the database. - CreateTableParse.test.ts covers the strict grammar both ways. Writing those tests immediately found two more bugs, fixed here: - Index metadata was keyed by table name alone, so opening PEOPLE in one database silently activated an index defined on a different database's PEOPLE — pointing record order at a column that need not exist there. Now keyed by (db, table, tag). Existing definitions are adopted into the database that owns the table; ambiguous ones are dropped and must be recreated with INDEX ON. - A bare `INPUT "..." TO var` at the REPL discarded the typed value: form-submit only stored values when a continuation existed, which never happens for a single statement. Also removes the input-request/input-response message types (declared but never sent or handled), teaches the New table wizard to emit NUM(p,s), and adds `npm run coverage` so untested modules stop hiding.
test: strict CREATE TABLE, demo schema pins, grid message coverage (#50)
DATEADD(date, n) returns the ISO date n days later; n may be negative. W3Script had no date arithmetic at all — CTOD/DTOC only reformat and YEAR/MONTH/DAY only decompose — so there was no way to say "the day after this one". demos/overtime.prg (#46) needs it to derive each TIMESHEET.WORKDATE from the week's Monday. Computed in UTC, so month, year and leap-day boundaries are exact and no local timezone offset can shift the day. Impossible ISO dates return '' rather than rolling over, matching WEEK(). WEEK() already parsed dates exactly this way, so that logic is extracted into a shared parseDateUTC() rather than duplicated. Registered in Parser's BUILTIN_FUNCTIONS, and covered directly, through the parser, and end-to-end in the REPL — the three-way coverage the #4 built-ins lacked when they shipped broken.
feat: DATEADD() date-arithmetic built-in (#52)
An overtime tracker, and the showcase for this release's engine work: TIME(15) columns validated per-cell as you type in BROWSE, WEEK() for the ISO week number, and DATEADD() to walk a week's Monday to Friday. Five linked tables (EMPLOYEES, SCHEDULEDAYS, TIMESHEET, WEEKSUMMARY, LEAVETAKEN) across five work areas with relations into EMPLOYEES. Each employee has their own weekly schedule, so standard hours are a real per-employee sum rather than a flat 40. Overtime is banked per week and drawn down as leave; the balance is computed live from the source rows, so re-editing a week cannot leave a stale running total. Two engine constraints shaped the implementation, both verified before writing the program: - SUM ... FOR and SET FILTER are spliced raw into SQL and cannot see a memory variable, so the per-employee balance accumulates in a DO WHILE / IF loop rather than the crm.prg SUM ... FOR ... TO idiom, whose condition is a literal. - Field references only resolve where the row cache is primed (IF, DO WHILE, @ SAY), which is why the time arithmetic lives inside the loop body. Times are HH:MM text, so worked hours convert to minutes first: (TIMEOUT - TIMEIN) - (BEND - BSTART), rounded to two decimals. Seeds a grouped report and is reachable from the splash screen, HELP, and the Assistant (Programs -> Run Overtime demo). The DemoSchemas golden pins from #50 caught the new tables immediately and now cover them.
feat: demos/overtime.prg — Overtime Tracking demo (#46)
…nshots The per-cell validation error (#45) was never visible. Grid cells set overflow:hidden for the ellipsis on long values, which clipped the absolutely-positioned tooltip to nothing — an invalid edit showed a red border and no explanation of what was wrong. The e2e tests passed the whole time: toContainText and toBeVisible do not account for clipping by an ancestor's overflow. The assertion now uses toBeInViewport(), which is backed by an IntersectionObserver and does. Verified it fails against the unfixed CSS (viewport ratio 0). Found by looking at a screenshot of the feature rather than trusting a green test — now recorded in CLAUDE.md's test-discipline notes. Also regenerates every screenshot and the README demo GIF against v1.2.0 (they still showed the v1.1.1 banner), and adds a new screenshot-grid-validation.png plus its capture step and a README section for it.
fix: BROWSE validation message was clipped away; refresh docs + screenshots
Date the v1.2.0 heading, drop the "in progress" marker from the roadmap, and record the screenshot/GIF refresh.
release: finalize CHANGELOG and roadmap for v1.2.0
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.
Ships the v1.2.0 milestone (#43, #44, #45, #46, #50, #52 — all closed).
package.jsonis already at1.2.0;v1.2.0gets tagged on this merge commit.Highlights
TIMEcolumns (#43) —TIME/TIME(n), storing canonicalHH:MM. TheTIME(15)qualifier requires quarter-hour minutes and is enforced on write.WEEK()(#44) andDATEADD()(#52) — ISO-8601 week numbers, and the day arithmetic W3Script simply didn't have. Both computed in UTC, so month/year/leap-day boundaries are exact.BROWSEper-cell validation (#45) — an invalid edit keeps the cell in edit mode, outlined red, explaining why; the error clears as you fix it. Rules live in one shared module and run on the client for instant feedback and on the server as the authoritative check.grid-editpreviously bypassed the Executor entirely and wrote to SQLite unvalidated.demos/overtime.prg(#46) — an Overtime Tracker that exercises all of the above: per-employee weekly schedules,TIME(15)timesheets edited in the validated grid, a live overtime balance (banked minus leave), a grouped report, CSV export.Test hardening (#50) — after two bugs shipped through a 283-test suite, this closes the blind spots that let them: a strict
CREATE TABLEgrammar (it used to absorb unknown tokens and invent columns), golden pins on the demo schemas, coverage for every grid WS message, andnpm run coverage.Bugs found and fixed along the way
Several were pre-existing and user-visible:
NUM(8,2)created a phantom column literally named2— the shippedPRODUCTS,DEALSandSALESdemo tables all carried it.PEOPLEin one database silently activated an index defined on a different database'sPEOPLE, pointing record order at a column that need not exist there. Same defect in the new column-type store. Both now keyed by(db, table, …), with a data-preserving migration.INPUT "…" TO varat the REPL silently discarded the typed value.BROWSEvalidation message was clipped away byoverflow: hidden— users saw a red border and no reason. The tests passed becausetoContainText/toBeVisibledon't account for ancestor clipping; they now usetoBeInViewport().Verification
npm test— 379/379 vitest (was 265 at v1.1.1).npx playwright test— 94/94 (was 73).npx tsc --noEmitclean. Both CI jobs green on every PR into this branch.Screenshots and the README demo GIF are regenerated against v1.2.0, including a new
screenshot-grid-validation.png.Carried forward: #34 (Assistant: surface JOIN + work areas) moved to the new v1.3.0 milestone — it predates this line and was never in scope here.