Skip to content

Known limitations, and points to address

  • Program overlap depends on the selected route. Only completion and render work on a resolved program setting whose number of copies is above 0 skips the per-plug-in call slot. Key routes, status, options, lifecycle hooks and programs at 0 retain serial admission; an immediate call can answer busy while another serial call holds that slot. All roots still share the app’s five-call bound, and a program render resource queues pictures beyond its number of copies. At 0 copies a picture waits for the chat’s process to end and reads as running; it retains its resource’s capacity of one and one of the render ceiling’s three capacities while waiting, but no runner slot. Only process end or the job’s Stop wakes a pending wait; release does not. A chat turn taking the freed process before the render reaches the door can still make the picture fail busy.
  • Forms exist for the project file, characters and locations. Every other type is edited through the JSON editor until it has a form of its own. On a character or a location media and preview belong to the media card (§9.3) and have left Other attributes; the media keys it does not own (picks, voice.provider_bindings) are read-only there, and a value of another shape than its box’s is changed through the JSON editor.
  • §13.3’s block table is only half drawn. The format asks a reader to collapse unchanged blocks (6 unchanged), to mark the changed words of a block both versions have, and to leave a gap where the other version has a line this one does not. A comparison here draws the blocks as a diff: with Show changes only on, the rows where the two versions are equal are hidden and the section ends with n unchanged; with it off, every block is a row of its own; a block moved between the two versions is a row out, where it was, and a row in, where it is. What is still outstanding is the marking of the changed words inside a block — a changed block is a row with both texts whole — and a gap in place of Not in this version. What the table says copy-left does in each of the four cases is implemented exactly.
  • No per-attribute compare of two epochs of one entity (§13.3) yet: Compare’s left column is the viewer’s latest at the epoch on screen, so nothing compares across epochs. Copy-left itself is built (§9.3).
  • Copy-left does not move what two versions disagree about the shape of — a key one holds as an object and the other as a list or a sentence — nor an empty value of any kind, since an empty value is how this application removes a key. The JSON editor is the way through either.
  • A save re-reads the whole folder. Fine for hundreds of files; a project of thousands would want an incremental index.
  • Concurrent edits are refused, not merged. A file changed since it was read is never overwritten (StaleFileException); the user re-reads and repeats the change. Two people editing the same file at once still need Git.
  • Toggling the star off deletes the pointer. The previous official choice is not remembered; setting it again is one click on that version. A pick that tracked official then falls back (own version, else the sole author), as §6.1 says. Dragging an epoch to the top makes it the default without confirmation.
  • A period that does not reach an AI draft of the project. builtin/declare-epoch sets the period it writes in the person’s AI draft too (§7.3). Not where the version in use is another author’s, which it forks and picks, so that the older draft is no longer the draft; not for a label already declared, which writes nothing; not where the draft’s write failed, which the answer says; and not for a period a person adds or changes by hand. Picking such a draft loses that period from the declarations, the files staying as they are. A period’s Discard reaches the drafts its own chat’s record holds, so a draft a later chat forked with the period in it keeps it, and picking that draft brings it back.
  • The assistant pins the person’s version before its fork. Where nothing explicit decides the person’s version in use — their own latest, or, where nothing is official and they have no version of their own, the sole owner’s (§6.1 step 3) — that version is written as the person’s own pick before the assistant’s fork makes their draft (§7.3), so that the unpicked draft is not in use and they keep what they had — and keep it when the owner later makes another version official, where a person who said nothing would have followed (§13.2).
  • Except for a file an official pointer names, which resolves through official whoever is reading (§5.4), the viewer is the referencing author. §6.1 resolves from the point of view of the file that holds the reference; the application resolves everything from the viewer’s point of view, picks included. The two agree whenever the viewer browses their own work.
  • The filmopen.ai account supplies sign-in and the account’s usernames; it does not sync projects or fetch provider keys. Google Drive sharing uses its separate Google account. Browser sign-in works on Windows (the scheme is registered on first use) and on the web; on macOS and Linux only the e-mailed code works until those builds exist. The OAuth round trip has been verified by reading the plugins’ sources, not by a live sign-in from this checkout.
  • Project media is local (§6.9), in the project’s flat media/ folder or, on the web, the sample’s in-memory overlay. Old manifest locators have no effect. Sharing sends copies to a configured folder or Google Drive target and receives files whose media names are absent locally; the target never becomes the local media root.
  • No Open from Drive… yet. Receiving fills a film already opened from its local folder and bound to its sharing target. There is no account-folder browser or command that creates that local folder from Drive. A change at the target reaches the film at its next reconcile; nothing polls the target.
  • Sharing never deletes. A file removed from the film stays at the target, Stop sharing leaves the shared folder as it is, and the shared copy’s manifest keeps the sync it was sent with.
  • Sharing a film with another person needs Google’s Picker or a scope wider than drive.file: with drive.file another person’s FilmOpen cannot see a folder this one made.
  • Two sends of one new media name at once, from two machines, can each make a file at Drive: Drive allows two files of one name, and the lookup before a send cannot close the gap between them. A document is replaced in place and has no such race.
  • A document over 5 MB does not reach Drive, which takes at most that much in one request; FilmOpen’s documents are a few kilobytes. Media reach Drive without a media type (application/octet-stream), so whether Drive previews them is Drive’s guess.
  • The Android sign-in’s redirect door is configured but has not run against Google.
  • Mobile “show in folder” (iOS shareddocuments://, Android content://) has not run for a shared-storage path: Android is configured but the app’s own files are app-private and the Android branch answers false for them; iOS is not configured.
  • The shared library is not updated by the app: it is seeded once and then left alone.
  • The model catalogue is read only in part. pubspec.yaml bundles assets/models/ and assets/models/platforms/ whole (the Project Specification §14.6–§14.9): Provider keys reads the platforms (§6.7), and dictation the entries of its two speech services, stt-gpt-live-transcribe.json and stt-grok-transcribe-20.json, with their platforms’ files (§9.6). No other entry is read yet, and a model (mo) or platform (pl) library entry is still written by hand.
  • Warnings are not grouped; the manifest warnings join the same list.
  • Resolution through official for official files (§6.1 second paragraph) is implemented in the model and applied where a version’s own references are rendered — today only the Script tab’s speakers. Everywhere else, including the tree and the pages, the reader keeps browsing in their own world.
  • SRT, OTIO, timeline export and every other §15–16 concern are out of scope so far; of the screenplay formats, Final Draft, Fountain (a .txt included) and text PDF are imported (§9.2), and nothing is exported.
  • Media: a picture is shown, and a clip or a sound plays wherever the scenes’ engine plays (§9.3, §19.16): Linux and Windows on MLT, Android on Media3. The web, whose engine is the fake, says Coming soon, offering Show in folder where the file is on disk; so do bytes a session holds only in memory. A clip’s thumbnail is the kind’s stock picture, not its first frame.
  • Narrow layouts are supported down to about 320 px of window (§9.1): below that a comparison scrolls sideways rather than fitting. The web build is a preview, not a target. The app runs on Android — the Galaxy S24 Ultra at 411 × 891 dp and the emulator at 448 × 997 — where the rail and the tree are one drawer and the system’s back — the browser’s Back in the web preview too — closes it (the dashboard’s); the widths are verified in tests (360, 375 and 411 among them), in the browser preview and on the emulator.
  • Spanish is a first translation, not yet reviewed by a native speaker, and frozen: English is the one language maintained, so a message Spanish lacks reads in English, and while FilmOpen is in development the one language shown, the language control held (§10).
  • Windows path length limits are not checked before writing a new project folder.
  • Warnings are per load and are not persisted or grouped.
  • A development-mode edit of a library file is permanent. The shared library is read by every project on the machine and seeded only when empty; nothing restores the bundled copy.
  • A re-read during a change of library folder may bring the old library back until the next open; two forks of one version at the same moment tell the second person the file changed underneath.
  • A draft whose form has gone can still be refused, and only the log says so. A form left during a write writes what was typed meanwhile after that write; when the re-read of that write was overtaken by another, or when the write failed and its one retry is refused, the later typing is refused as stale with no save status left to show it.
  • A session copy is lost when it is left. Choosing the sample again, or closing the app, drops a session copy’s edits; there is no exit guard (disposition). The tree header says session copy — not saved to disk, and Copy to a folder… keeps it.
  • The agent layer controls the app on Windows and on a headless Linux runner (§4.3, §7.1).
  • An MCP client drives an agent build through the server’s tools; this is proved on Windows and on Linux under Xvfb.
  • launch takes size (<width>x<height>, 1280×720 when omitted) and, on Linux, home (fresh XDG folders otherwise).
  • Every product build runs the no-op host.
  • scroll reveals lazy controls with vertical semantics actions in an identified region; horizontal scrolling and arbitrary drags have no op. Ambiguous vertical lists require a narrower identified region. A missing identifier after bounded scrolling is a failure.
  • page_shot shows the whole view as the app paints it from its own render tree — every route, dialog and menu — and screenshot does too, though neither shows the native window frame or a native dialog such as the folder picker. A picture shows what the screen shows, an e-mail address included. An agent build closed by hand or killed keeps the settings an agent gave it; only quit puts them back. The web build has no driver; agents use the desktop binary. Dialog text boxes (New project, the label, the JSON editor) take no set_field; enter_text fills the one that has the focus, and nothing tells whether one had it. size is the view’s size on both runners, to within a pixel on Windows.
  • The issue recorder’s known limits (§6.8). Replay’s gaps — a key, a drag, a scroll, a long press, development mode — stay manual steps printed by scripts/replay_tape.py, never replayed. A picture’s measure does not see a value a box shows but cannot render (a height typed as abc). Replay resolves against the machine’s own shared library, not a copy of the one recorded. The web build keeps its tape in memory and writes nothing. The Linux agent walk has not been run for the issue recorder.
  • Sending a recording’s known limits (§6.8). The ticket and the upload are proved against in-process stand-ins for the website and the boxes, and no send has yet reached a real support box. The zip is made in memory, on its own isolate, so a folder at a box’s limits is held whole while it is zipped. A description being typed when the window closes is lost, though the recording is kept and listed. A box that refuses the zip before reading it is heard only once the zip has gone, and one that answers more than a minute after its last byte is taken for a stalled one, so the next box may be sent the zip too. A commentary’s words can stand a picture off where a speech service ends a turn by itself (xAI) or leaves a commit unanswered; a word begun within 0.2 s of a picture can lose its start; and the words being said as the window closes, or as a session reaches its time limit, are lost.
  • Folders in the script. Nothing deletes a version: a folder or scene taken out of every list stays under Not in the script until its file is removed by hand. Two people who add at the same time get the same number, and two unrelated first versions of one short name then read as two versions of one scene. A pinned entry is unpinned only in the JSON editor. A scene’s blocks: a drag of a block reorders only within what is on screen, the page not scrolling under it (Move up and Move down reach any place); an agent sets a block’s speaker through set_field, its pull-down’s items carrying no identifiers of their own. The move dialog puts a row at the end of the list chosen; a place among its rows is a drag’s, or Move up and Move down afterwards. Drag and drop is the tree’s and a scene’s blocks’ alone: a Contents card reorders by its rows’ menus; the agent layer has no drag — every move a drag makes is a menu item — and the issue recorder prints one as a manual step. Compare at a narrow window gives a folder’s and a scene’s rows the two narrow columns every form’s rows have had there.
  • Identifiers name the doors, not every control. Compare’s cells (built outside the form’s field layout), the Settings show folder buttons and the sign-in dialog’s secondary buttons carry none yet; tabs never will (§13).
  • Android’s background transfers end with the activity (§6.14). The foreground service stops when the activity is destroyed — a swipe-away from the recent apps, or Back below Android 12, included — and on Android 15’s daily data-sync timeout; the journal resumes the work at the next open. No notification permission is asked, so on Android 13 and later the service runs without its notification unless the person allowed notifications. iOS has no background host yet, and the desktop asks before closing rather than lingering in a tray.
  • Development mode exists (§9.5) and is on by default outside a release build: another author’s version, official without being a director and the shared library warn instead of refusing. What stays enforced near production is not yet decided; the rules that protect files are not governed and refuse in every build.

A shot’s camera_character (Project Specification 1.22) is kept, not yet read. Every writer keeps it as a key the app does not read (Project Specification §18.2), and copy-left moves it as it moves any other value: no page renders a shot yet, so the treatment a render takes never meets a shot’s own camera, which the page that renders shots will prefer. A scene’s render — whose Style the treatment already takes — waits for such a page too.

Service media stores are not connected. Takes are discovered in media/ through the project’s local or in-memory source; an entity’s reference can also resolve through its import batch record when the source listing lacks the take.

Each point says what could go wrong, and where it is handled or still open.

The plug-ins’ app: strings (§6.11, recorded on 17 September 2026). A plug-in borrows an app string by its ARB key through filmopen_l10n’s generated appStringsByKey — every string of the app a second time, kept in step by scripts/gen_app_strings.py and a test that fails when the map is stale. Decided: the next change that touches localisation bundles the two ARB files as assets and reads them at run time through the plug-ins package’s own ARB reader, one read per language on the first app: lookup, removing the map, the generator and the test. Until then the generator runs after every ARB edit. No stock plug-in uses an app: string.

The device’s key in hardware (§6.6b, recorded on 19 September 2026). The key is made in Dart and kept in the credential store, which encrypts it for the user; a program running as that user can read it. Made inside the Secure Enclave or the Android Keystore it could never leave the device, which is what the website’s R4 (a portal token bound to a device) and R10 (share) will lean on. It needs native code per platform; the Keystore and the Enclave sign in DER, which the app then converts to r || s (the contract’s K4). The id would change with the key, so the move is a new device in the person’s list, once.

The usage log (§6.7).

  • Live sessions. The catalogue prices a live transcription per minute of audio.
  • Where the cost comes from. OpenAI’s SDK gives conversation.item.input_audio_transcription.completed a usage field, in tokens or in seconds of audio. Whether gpt-live-transcribe fills it, and in which unit, is still to be checked. Until then, the cost is the catalogue’s price times the minutes sent.
  • A crash loses what followed the last running record, and the chain stays open, showing that last cost.
  • Through filmopen.ai. How a brokered session is settled is open (Portal Specification).
  • Double counting.
  • On one machine. Two copies of the app can share the usage folder, and both ask a platform about the same queued job. Each may write the job’s final record, but only the chain’s latest record counts, so the job is counted once. The step that builds the log proves this with two writers.
  • Across devices. When records reach filmopen.ai, a record keeps its id, so a use is counted once however often it is sent (Portal Specification P10).
  • Open chains. A call that timed out, a cancel the platform did not confirm, and a crash each leave a chain open. Activity’s Usage counts it by its counted record, as Totals says: a started or submitted chain’s estimate as pending or possibly charged, and a running chain’s cost so far as spent.
  • A platform may still finish, and charge for, a call the app gave up on. OpenRouter stops generating on a cancelled stream only for providers that support cancelling; its streaming page names providers that do not, Google among them. fal may complete a job after a cancel is requested.
  • A started chain whose platform never returned an id cannot be asked about later.
  • A key replaced while jobs are open. Once the start-up check of §6.7 is built, the app asks only about chains whose key matches the one now stored, so a chain submitted with a key since replaced stays open until the user checks the platform.
  • Corrections carry the status of the chain’s counted record. Whichever record is latest counts, so a correction carrying an earlier status would reopen a finished call.
  • A record that cannot be written after a call has started. A submitted or running record waiting for the lock is retried in memory. A crash before it is written loses the platform’s request id, so the job cannot be asked about.
  • A file cut short by a crash could join the next record to its broken line. The writer first ends such a file with a line feed.
  • A lost first record. A chain whose first record is missing, from a line cut short or a file removed by hand, still counts once through the ref its records share, but in the month of its earliest remaining record.
  • Version skew. A build that shares the folder with a newer one skips every chain holding a record with a newer v. It may leave out calls the newer build recorded, but it never counts part of one. Builds that share a folder should share a record version.
  • Clocks. A writer keeps a chain’s times in order, a millisecond past the chain’s latest record it has read when its clock has stopped or gone back. Two copies of the app writing to one chain at the same moment can still tie; then the greater id counts, and both records carry what the platform said.
  • Estimates. A call whose request does not bound its cost is estimated at the smallest amount it can cost, so its pending amount understates.
  • The price row. A price-basis cost cannot be recomputed later without the catalogue’s price row that applied, and a record does not yet name it.
  • Money against the website.
  • The website takes provider costs to ten fractional digits and has not chosen a live currency.
  • Device records use twelve digits and the platform’s currency, and they never settle (Portal Specification P11).
  • A later format for brokered jobs must follow the website’s rule.
  • The lock on Windows is mandatory, so it is taken only on .lock, which no reader opens. A writer that cannot take it within ten seconds does not start a paid call.
  • A folder copied to another machine keeps its device.json, so two machines share a device id until one of them deletes it. The file is made again only when it is read whole and holds no valid id, so a scanner that briefly holds it does not change the id.
  • Names in purpose. File stems can hold people’s names. What a record may carry off the device is open (Portal Specification).

Keys (§6.4, §6.7).

  • One file holds every secret on Windows. flutter_secure_storage keeps the account’s session and every provider key in one DPAPI file, rewrites the whole file on each write, and deletes it when it cannot decrypt it.
  • Within one app, the plugin runs its calls one at a time, so a key save and the account’s refresh do not collide.
  • Two copies of the app writing at once can still lose one write. A failed decryption loses every key and the session together.
  • A key pasted into a chat, a shared file or a message is exposed, and should be replaced.
  • A key’s expiry. A platform key can expire, as OpenAI’s keys can. Nothing re-checks a key once it is stored, so an expired one keeps its Verified on state and fails where it is used; the user makes a new one and saves it over the old.
  • The tab’s states can outlive the keys. A store emptied by a failed decryption, or changed by another program, leaves the preferences’ states claiming keys the store no longer holds, and nothing reads the store behind the card, so the card says Verified on until the key is saved again.
  • Keys can outlive their states, too. A preferences file put back from before a key was saved, as an agent’s run puts theirs back, leaves a card saying No key while the store holds a key. The store is the source of truth — dictation reads the store, not the card — but nothing reads it behind the card, so the card says No key until the key is saved again.
  • A write the operating system never finishes. On Windows the plugin retries a failed file write without end, under its own lock. Each caller has ten seconds before it fails closed as timeout, but the stuck native call keeps the queue, so every call behind it times out without running, and the plugin’s lock can hold the account session’s calls too, until the app restarts.
  • Keys handed to devices by the portal, later. Signing deters copying but cannot prevent it, so a handed-out credential needs a spending limit where the platform offers one (Portal Specification P17).

Platforms.

  • ElevenLabs through fal, for now. fal’s voice design returns a voice id when it saves the voice, and fal’s dialogue endpoint takes a voice’s name or id. Open (Portal Specification):
  • whether fal’s single-voice speech endpoint takes a designed voice’s id;
  • how long a designed voice lasts;
  • what a design costs.
  • Pending platforms keep their files and notes, with their obstacles in the index, for later work.
  • Terms. Whether a portal that runs jobs, or hands out keys, for other people fits each platform’s terms is open for OpenRouter, fal, OpenAI and ElevenLabs (Portal Specification).
  • Results served only with the account key, such as OpenAI’s and Veo’s videos, would put large files through the portal (Portal Specification).

Dictation (§9.6).

  • Turns decided on the device. The gate’s defaults — three times the room, 0.8 s of quiet, 0.2 s of speech, a room of at least 50 that rises at most 3 % a frame and is held under a voice, and a turn cleared as noise where a second or more of it is still as even as a tone when it ends — are untried on real microphones. A voice quieter than three times the room is heard as no speech, and its audio is cleared with the quiet. A noise that is not steady, such as a television, is transcribed, and so is an even sound that stops, such as a beep. A voice whose frames vary by less than twice is taken for a steady noise: the room climbs to it, and a turn of it still sounding at Stop or a release is cleared. A steady noise’s first seconds are held, then cleared, before the room has learned it. dictation.ended carries the numbers that show these (§9.6), never the audio. Should OpenAI take voice activity detection for this model again, its catalogue entry says so first.
  • An answer that never comes. A commit the platform neither takes nor refuses holds a release or the apply button for its three seconds; the text then applied is what arrived.

Sharing sync (§6.14).

  • Settings configures a folder or Google Drive target; Share shows its jobs. Session copies must be copied to a folder first, and the web preview offers no sharing controls.
  • Google Drive uses drive.file alone. Choosing an existing folder shared by another person requires a Picker integration or an owner-approved scope change; neither is implemented.
  • Drive media publication checks for an existing name before uploading, but Google Drive permits duplicate names. Two clients can pass that check concurrently; it is not an atomic reservation.
  • Real Google Drive uploads, Windows sharing and the Android sign-in redirect require platform and account verification; stand-in tests and a Linux folder run do not establish those results.
  • Transfers send local-only files and receive target-only files without propagating deletions. A local media name is never replaced by receiving. Documents use their last agreed hash to distinguish a one-sided change from a conflict; Take theirs, Keep mine and Dismiss resolve or set aside a conflict (§6.14).
  • Concurrent sends can still replace each other’s document at the target. A document’s decision and target publication are separate operations, and the send contract has no atomic target-content guard. The ledger detects divergence visible when it decides; it cannot preserve an intervening target edit overwritten by a racing send. Receiving protects the local document with expectedText and decides again when that guard refuses the write.

The portal (Portal Specification §8).

  • Where usage records live without a relational database, and the change that asks of the website’s contract.
  • How provider keys are encrypted.
  • FilmOpen’s fee on a director’s own keys, and in the portal’s Usage tab.
  • Whether money stays in PostgreSQL.
  • Estimates against limits, and how records reach the portal.

An ordinary assistant receipt still pending when the application closes remains pending on the next start; no automatic receipt reconciliation runs. A never-answering credit request waits for the person’s Stop.