Review Before Write
English is immutable. AI creates proposals, not direct sheet edits. Applying an exact proposal is another explicit decision.
A detailed, source-grounded guide to what the application does, how it protects training content, why the architecture looks this way, and what its repository and live deployment actually prove.
ForgeLessons is a source-safe localization workspace for training content already held in Google Sheets. It helps an operator find strings, generate or write a proposed target, review the difference, and approve a single-cell write with a recorded read-back. It is not yet a general lesson-authoring system, a field-capture app, or a replacement for the workbook.
ForgeLessons moved to the private forgefx/forgelessons repository on 9 September. The local ForgeApps checkout still contains a dirty app directory, while its remote branches contain the retirement and migration-pointer commits. Treating that directory as current production would blend an old checkout, uncommitted edits, and a completed extraction.
f6f76a711 removed the workspace; e6e510fcf moved the pointer to docs/FORGELESSONS-MIGRATION.md. Standalone 7e3fa74ee established its independent workspace, and 0e790cfe5 reconciled the latest monorepo workflow.
English is immutable. AI creates proposals, not direct sheet edits. Applying an exact proposal is another explicit decision.
Google Sheets holds the cell values. Supabase holds the workflow, permissions, locks, attempts, reservations, and history.
The open page dispatches one awaited cell at a time. A durable job is not an autonomous background worker.
Miguel is identified as project lead; Adam Kane provides support. The standalone README and About page make those roles explicit. The app’s immediate audience is the team maintaining equipment-training text and regional translations, not trainees consuming a learning-management course.
Evidence: README.md, MIGRATION.md, src/pages/AboutPage.tsx, server/localizer.ts.
These are separate facts, not a single “ready” badge. The investigation pinned source at a456b31c9e3e5fb7428a466a067ac1339902d775 and read committed files with git show after discovering concurrent working-tree changes in the implementation checkout. Those concurrent edits are not included in this source baseline.
Package version 2026.09.09.3 at a456b31c9. The three commits after the integration baseline change only package.json version text; the inspected diff from 0e790cfe to this tip contains no workflow changes.
Vercel reported READY for 0e790cfe5, deployment dpl_B4NRvacSC4VahNUcQiEbetaG3fN5. The live header and footer both showed v2026.09.09.0. The later .1, .2, and .3 deployments were BLOCKED. Their exact blocker cause was not established by the deployment-list response.
Read-only Vercel API checks confirmed GitHub forgefx/forgelessons, production branch main, repository-root configuration, Node 24.x, and verified domains lessons.forgefx.dev and forgelessons.forgefx.dev. This is live configuration evidence, rather than assuming migration documentation proves the cutover.
/home loaded without personal sign-in. The workbook selector offered John Deere and Halliburton. John Deere exposed 44 tabs; its initial User Notifications tab showed 14 source strings, 0 missing pt-BR targets, 1 reviewed lock, and 0 changed-source flags. These are one tab’s point-in-time counts, not totals for the whole app or evidence of translation accuracy.
GET /api/state returned HTTP 200 application/json with readOnly:true, allowWrites:false, and AI unavailable to the guest. GET /api/jobs/step returned HTTP 401 application/json with “Authentication required.” That proves the nested path reaches an authentication boundary; it does not prove an authenticated POST runs a job.
No credentials were entered, no authenticated workflow was exercised, no paid generation was started, and no Google or Supabase records were mutated. No app build or test run was launched for this report. Historical validation claims are labeled as such. The report and screenshot files are the deliverables; app code, branches, and index were left untouched by this researcher.
Observation window: 9 September 2026, approximately 21:37 UTC. This is a historical snapshot of a rapidly changing app. Later releases, including concurrent work elsewhere, may supersede it.
A registered-workbook selector is above searchable tab navigation. The UI prioritizes John Deere by a stable known identifier, then sorts other titles and IDs. It remembers an explicit selection in forgelessons.selectedWorkbook, but restores it only if the API still authorizes that workbook. Storage failure degrades gracefully; a saved preference never grants access.
The heading names the selected tab. Metrics, locale selection, search, status filters, and a row table support triage. English and target text remain side by side. The live guest view shows Strings only, while paid and editing controls are disabled. The layout is a localization workbench, not a lesson player.
The About page has Workflow, Architecture, Tech Stack, Data Schema, Design Choices, Development, and People & Links tabs. Its source supports arrow-key navigation, Home/End keys, tab/tabpanel semantics, and a recognized initial URL hash. These are documentation sections; they are not additional application workflows.
Evidence: fresh production screenshots; src/pages/LessonsPage.tsx, src/pages/AboutPage.tsx, src/components/SiteHeader.tsx.
Administrative registration verifies Google metadata before creating a workbook record. Existing registration alone does not grant anonymous access: shared reads also need an explicit server-side allowlist. Metadata selection and tab requests are tied to the selected workbook. Tab changes clear stale displayed state; request versioning prevents a late response from replacing a newer tab.
The supported locales are pt-BR (Brazilian Portuguese), es-419 (Latin American Spanish), and fr-CA (Canadian French). The mapper recognizes existing workbook header aliases such as Portuguese(pt) and Spanish(es). Those are mapped, not renamed. A missing locale column is an explicit unsupported target, not permission to alter the spreadsheet schema.
Search checks key, English, and selected target text. Filters expose missing translations, changed source, reviewed/locked targets, and protected formula cells. Visible rows are paginated at 25 per page. The source distinguishes target existence from correctness: “translated” means nonempty, not linguistically validated. Source-change and reviewed-stale indicators are separate concepts.
Edit opens a dialog with locked English and an editable target. Creating a proposal stores the intended text and original cell identity/value/hash; it does not write to Google. The proposal panel shows source, before/after, locale, type, status, and timestamp. A user separately confirms “Approve & write this cell.”
A nonempty target can be marked reviewed. The lock records source hash and target value; stale review is detectable when either changes outside the app. Protected or reviewed targets are excluded from AI candidate selection. Unlock is explicit. A lock expresses human review, not an AI quality score.
Localize missing selects eligible blanks. Spell & grammar selects existing eligible targets. AI check scopes proofreading to one source row. Redo lesson regenerates existing targets and blanks across the whole selected tab; Redo step scopes that operation to exactly one keyed row. Every mode targets one selected locale and produces proposals rather than direct edits.
Operators supply manufacturer, machine, reference excerpts/instructions, and source/locale/target glossary entries. Workbook defaults can be replaced by a tab-specific context object. The UI can append a proposal correction to the glossary editor, but it must be reviewed and saved to persist. Existing jobs keep their frozen context rather than inheriting later corrections.
History records write intent and verified or uncertain outcomes. “Preview undo” creates a reverse proposal only for a verified write, subject to current key/column/tab/value checks. Undo is not an immediate rollback button. The reverse proposal requires its own exact apply approval and read-back.
The server returns proposals, jobs, and history in workbook-scoped pages of 200 records, sorted through the store. “Load more workflow records” appends another page. The browser separately filters displayed workflow records to the current tab. This is independent of the 25-row translation-table pagination.
The current committed router redirects root to /home, serves the localization page and /about, and shows a not-found page elsewhere. It retains shared theme/zoom/tooltips conventions. A standalone source repository, CI configuration, and migration guide now replace dependency on a sibling ForgeApps checkout.
Evidence: src/pages/LessonsPage.tsx, src/App.tsx, server/http.ts, server/domain.ts, server/ai.ts, server/localizer.ts.
Workbook, tab, locale, row selection
Review dialogs and explicit confirmations
One-cell job dispatcher
Guest-read gate or authenticated membership
Origin checks → Localizer / AI / Jobs
Lease, intent, reservation, validation
Authoritative keys, English, target cells
Formatted reads + formula reads
RAW single-cell write and fresh read-back
App-owned workflow tables
Membership and global lease
Fenced commits and paid-attempt reservations
Server-selected model and rates
Strict JSON translation output
Provider usage and response receipt
The browser does not hold the Google service-account key, OpenAI key, or Supabase service-role key. The API exposes browser-facing Supabase configuration for sign-in but performs business mutations server-side. Service configuration is injected through Doppler; a source extraction is not a migration of those external services.
Declared dependencies include React 18.3.1, React Router 6.30.2, TypeScript 5.9.3, Vite 5.4.21, Tailwind 3.4.19, Radix, next-themes, and Supabase JS. These are declared ranges, not a fresh lockfile-resolution audit. Node 24 is the standalone engine. The local workspace retains packages/tooltips and packages/vite-react-singleton; their previous package histories were not imported.
A shared handleApi function is called from the catch-all entry and explicit nested job entries. Vercel rewrites API requests to the server function before a negative-match SPA fallback. Functions allow 300 seconds. Local Vite development provides the API; static vite preview does not. There is no cron, detached worker, or SQLite runtime in this delivery.
The server signs an RS256 service-account assertion, exchanges it for an OAuth token, and uses the Sheets API. Each tab is read once as formatted values and again as formulas so displayed strings do not conceal protected formulas. Writes use valueInputOption: RAW and an escaped sheet title plus exact row/column address. Requests have timeouts; the adapter includes per-instance throttling.
AI uses the Responses endpoint, strict JSON Schema containing only a translation string, store:false, and a standard/default service tier. Instructions explicitly treat source text, glossary, and context as untrusted data. App-prefixed settings override legacy localizer settings. The actual current model is environment-selected; this report does not infer live authenticated provider configuration from documentation or guest status.
Evidence: package.json, MIGRATION.md, vite.config.ts, vercel.json, server/google.ts, server/config.ts, server/ai.ts.
ForgeLessons does not copy the workbook into a new canonical content database. It stores the supporting workflow by typed record kind and identifier, then reads current Google values when validating a proposal or executing a step. This separation allows the existing sheet workflow to continue, but it also means freshness and external-edit conflicts are fundamental design concerns.
Composite primary key (kind,id), JSONB payload, update timestamp, and a workbook/kind lookup index. Record kinds are constrained by SQL.
Composite key on Supabase user and workbook. Role is member or admin. A user account alone does not authorize a workbook; registration requires admin membership.
A singleton row stores owner and expiry. Claim grants a 300-second lease. Mutating operations carry the owner token; expired or superseded owners cannot commit.
The four app RPCs are forgelessons_claim, forgelessons_release, forgelessons_commit, and forgelessons_reserve. The migration enables row-level security and revokes direct table/function privileges from anonymous and authenticated browser roles, granting the service role the needed access. The application then implements user/workbook authorization before using that privileged backend.
The store commits explicit record batches under the lease rather than loading and flushing an entire database snapshot. Reservations and write intent become durable before their external side effects. A lease reclaim is not permission to replay an uncertain Google write or paid request. This is a deliberate safety tradeoff: bounded serial execution and manual investigation are preferred to duplicate work or silent data corruption.
Evidence: supabase/migrations/20260908_forgelessons_workflow.sql, server/store.ts, server/auth.ts, server/localizer.ts.
/home. The browser obtains sign-in configuration and then requests state.POST /api/preview.POST /api/apply with confirm:true.writing atomically before the Google request.A “lesson” is the entire selected workbook tab, not the visible search result or current page. A “step” is one source row. Redo includes both existing text and blanks for the selected locale, while respecting reviewed/formula protection. Invalid or conflicting scope is rejected instead of widened. Queues larger than 10,000 cells fail explicitly.
Redo Start approves the frozen queue across durable bounded chunks. Missing/proofread use a per-run allowance and require explicit resume when that allowance is exhausted. The operations guide describes a 20-cell configured chunk/run ceiling and a 1,000,000-token daily ceiling; actual limits are environment-dependent. Redo does not remove daily caps, skip an unresolved row silently, or turn into automatic sheet writes.
Mark a nonempty target reviewed only after checking it. A later mismatch makes that review stale. To correct a locked value, explicitly unlock it. To reverse a verified write, preview undo, review the resulting reverse proposal, and separately apply it. To reuse a correction, add it to the glossary editor and save the appropriate workbook or tab context; a running job continues with its original snapshot.
Evidence: src/pages/LessonsPage.tsx, server/http.ts, server/shared-read.ts, server/localizer.ts, server/ai.ts, OPERATIONS.md.
The mapper searches the first 20 rows for supported Key/English headers. Duplicate alias matches are rejected as ambiguous. Blank key/source rows and DateVersion/SemanticVersion control keys are excluded. Formula detection comes from the separate FORMULA read, not a guess based only on display text.
Targets must contain 1–20,000 characters. Formula-like text beginning with =, +, or @ is blocked. Protected number/unit/placeholder/escape/markup token sets must match; markup order must match separately. Casing heuristics preserve all caps, lowercase, title case, and sentence case patterns. These are deterministic guardrails, not a semantic proof of translation quality.
Each proposal is tied to the original value, source hash, key, row, tab title, and column. Changed data blocks apply. Historical undo has its own schema checks. An unresolved proposal can block regeneration for the same cell; uncertain writes block a second proposal from bypassing investigation.
Shared GETs are explicitly scoped and read-only. Other operations require a validated Supabase identity and workbook membership; registration needs admin. Mutation routes check JSON content type and the allowed production origin https://lessons.forgefx.dev. The alternate verified domain is not automatically added to that origin allowlist in the inspected source.
The estimate uses serialized UTF-8 payload byte length plus a buffer as a conservative input-token proxy, and the configured maximum output. Reservations occur before the call and remain across failures. Reported actual usage uses returned token counts and configured rates; it is not provider-invoice reconciliation. Response receipt data strengthens auditability but does not eliminate uncertain transport outcomes.
The AI instructions tell the provider to treat glossary, references, keys, and source strings as untrusted data, preserve product/manufacturer names and technical tokens, and return only JSON. The deterministic validator checks several structural invariants afterward. Human review is still required for technical meaning, natural regional phrasing, and terminology consistency.
No cross-system transaction exists. The Google checks are optimistic. An external editor can race between the app’s final read and write. The database lease coordinates ForgeLessons operations, not Google’s other editors. Exact read-back detects many mismatches after the event; it is not a compare-and-swap guarantee.
Evidence: server/domain.ts, server/auth.ts, server/localizer.ts, server/ai.ts, server/google.ts.
The history has two repositories and two sets of hashes. Extraction rewrote the app’s retained commits. The standalone migration file says eight app-scoped commits were retained at extraction and the local shared packages were included as snapshots. Do not cite a rewritten standalone SHA as if it were the same identifier in ForgeApps.
The first visible app commit lands the original Sheet Localizer workflow and the new ForgeLessons app in the monorepo on 8 September. The intended change is architectural: keep Sheets authoritative and preserve review protections, but replace the earlier local SQLite workflow storage with Supabase and adopt the standard React shell. Because the first import contains a substantial working app, this Git timeline is not a minute-by-minute account of earlier design or implementation.
Follow-up commits address emitted server imports, local entry, About documentation, API fallback handling, shared reads, optional login, a second workbook, and workbook-scoped requests. The sequence shows why homepage uptime was not enough: an HTML SPA fallback could impersonate a successful API response, and a rendered sign-in screen could still conflict with an agreed shared-view workflow.
Standalone 7e3fa74ee removes dependency on the larger app workspace, adds local packages and standalone checks, and preserves the app history. c65c43766 explicitly routes API requests to the server function. 0e790cfe5 then integrates the more recent remote monorepo work: redo queues, remembered workbook choice, shared reads, header authentication, scoped provider settings/receipts, and explicit nested job functions. This reconciliation matters because the extraction source had lagged the remote app work.
The .1, .2, and .3 standalone releases are version-only commits at the inspected tip. Vercel marked all three blocked while the reconciled .0 deployment remained ready and visibly served .0. This is a deployment-state discrepancy, not evidence that the newer version adds missing features. The inspection did not establish the specific deployment blocker cause.
First visible app import: React shell, API/domain/store/AI code, SQL migration, scripts and tests land together. This is the first preserved repository evidence, not proof that the idea began that day.
Corrects server imports to emitted .js paths, addressing the distinction between TypeScript source resolution and deployed Node output.
Registers the local launcher and adds loopback guest/operator entry. Local bypass is distinct from production anonymous read access.
Standardizes the About page and records hosted audio work; it does not establish an end-user narration authoring pipeline.
Expands architectural/project documentation inside the app.
Organizes About into accessible sections and identifies Miguel as lead.
Updates version to 2026.09.08.1 and records scoped budget approval.
Fixes API traffic falling into the SPA HTML fallback. A 200 shell response had not proved API correctness.
Restores explicitly allowlisted anonymous workbook reads, without anonymous proposals, administration, or paid generation.
Makes login optional for shared viewing and adds the logo-based guest entry.
Adds approved Halliburton workbook support and safer discovery behavior.
Scopes tab requests to the chosen workbook, preventing stale selection/request crossovers.
Adds scoped approved operations and explicit nested job routes, keeping business logic in the shared authenticated handler.
Remove the app package, launcher/build registration, and workspace lockfile entry; move the surviving migration pointer outside apps/. These commits were visible on remote-tracking main/dev but absent from the stale local HEAD used for the initial filesystem inspection.
All commits reachable from the pinned standalone tip are listed below, including rewritten provenance. Timestamps retain their recorded offsets rather than silently normalizing chronology by display date.
Evidence: read-only git log, git show, and diff inspection in both repositories; MIGRATION.md. The monorepo ledger is path-scoped to the local HEAD history plus explicitly read remote retirement commits, not a claim to enumerate every wider-repository change.
The real guest workspace is usable for reading, but guest editing, administration, and AI are explicitly unavailable. Earlier curated product context describes a preference for no personal login. The current server boundary therefore does not establish acceptance of a fully no-login editing/AI workflow. Any future change must separately define allowed writes, paid usage, and administrative scope rather than making a logo click an authorization mechanism.
The standalone Git link and domains are configured, but three newer deployments were blocked at inspection. Resolve the actual platform restriction and verify the exact serving SHA/version after a successful release. Do not label source “deployed” because it reached main, and do not infer the blocker from author metadata alone.
The fresh pt-BR tab read included Out_Of_Bounds_Failure Name with Échec hors limites
and Out_Of_Bounds_Failure Description with Se movió fuera del área operativa designada.
Those appear to be French and Spanish text in the selected Portuguese target. The UI marked them translated because they were nonempty. This observation is a review lead, not a complete language audit or a claim that ForgeLessons authored those cells. No corrections were applied.
Interrupted paid calls and ambiguous writes intentionally stop progress and retain reservations. There is no blanket replay/reset button. Operational recovery needs evidence from provider receipts, live cells, and history before resuming; automatic retry would undermine the safety model.
A singleton lease serializes mutations across instances, and one running batch is enforced globally by the source. This limits concurrency across users/workbooks. Several operations scan whole record kinds in paged lists, and tab loads read whole tab ranges twice. The combination favors controlled workloads over high-throughput enterprise orchestration; no performance benchmark was performed.
Numbers, units, placeholders, markup, and casing checks help prevent damaging changes. They do not prove semantic equivalence or correct regional language. Strict English-derived capitalization can also be a constraint a reviewer needs to understand. A glossary and machine context improve consistency but do not replace qualified technical review.
Redo and localization reject absent target columns with an actionable message. The operations guide specifically keeps unapproved Halliburton schema additions out of scope. Resolving a missing locale column requires separate administrator-approved workbook work, followed by refresh.
Earlier product context describes creating lessons, adding/reordering steps, programmer notes, phone speech-to-text, and onsite photos. These are not implemented features of the pinned router/domain/workflow inspected here. There is no verified direct Word/SharePoint ingestion, Unity synchronization, narration generation, or field-capture pipeline in this app snapshot. Related tools or meeting discussions do not prove an integration exists.
Recommended acceptance order: verify serving release and authenticated boundaries; exercise one bounded real AI proposal in an isolated authorized test tab; independently verify a reviewed apply and a separately approved undo; then assess content-language quality and broader authoring scope. Those are recommendations, not operations performed by this report.
Read-only Git history and source inspection in both repositories; extraction pointer and reconciled standalone history; version-only delta; Vercel Git connection, Node runtime, domain verification, deployment statuses; real anonymous production Home/About/login renders; locale switching; live JSON state and nested-route authentication response; zero uncaught page errors in the captured browser session; no desktop horizontal page overflow on the captured app workspace.
MIGRATION.md records that local build, lint, 21 UI tests, and 49 server tests passed after reconciliation. This report did not rerun those commands or independently validate the historical count. Existing coverage artifacts in the stale monorepo were not used as proof of current test health.
The repository separates Vitest UI tests from Node server tests. Named suites cover domain/token rules, auth/origin, request body parsing, routes, shared reads, storage, budget reservations, proposal/apply/undo workflow, redo, workbook preference, and header authentication. Browser and Supabase verification scripts are separate from mocked unit tests. Merely having these files is not a passing test result.
Authenticated writes and paid calls; actual live provider model/rate settings; all workbook tabs and all target cells; translation quality across the corpus; credential rotation; production database migration internals; load/concurrency performance; complete no-login workflow acceptance; later concurrent implementation work.
The first browser-helper attempt timed out. The researcher used installed Chrome through Playwright for the actual production capture and report QA rather than presenting a simulated interface. The report embeds original captures; it does not reconstruct the app with invented example rows.
Source links below are pinned to the reviewed standalone commit, not a moving branch. The repository is private, so readers need GitHub access. Commit links in the chronology distinguish standalone and ForgeApps provenance explicitly.
The local research package is under /tmp/forgelessons-research/. This report uses history-live-evidence.json, history-commits.json, and the four live-*.png captures. The source generator is history-build.mjs; the browser capture is history-capture.mjs. The exact source snapshot is preserved through pinned citations rather than copying secrets or the private repository into this page.
This standalone preview is authorized technical/business context, not a confidential raw transcript archive. It contains no credential values, access tokens, staff-private context, or raw meeting transcripts. It is link-accessible external hosting, not an authenticated document. Crawler noindex/nofollow metadata is advisory and cannot enforce access control. The report should not be treated as a current health monitor.