Conventions for contributors, and third-party code
13. Conventions for contributors
Section titled “13. Conventions for contributors”- No user-facing text outside
filmopen_l10n(§10). The format and the store emit codes — but for the English words a render’s prompt names a treatment’s primary leaves by (treatmentPromptLeaves, the Project Specification §8.7), which are the model’s, and shown as it is given them. Widgets take strings fromcontext.l10n; providers fromlocalizationsProvider. - Every write goes through
ProjectWriter(orwriteNewProjectfor a new project), through the source’swriteText/deleteFile, followed byrefresh. No widget writes a file, and nothing edits the index in memory. - Write only as the viewer. A write without a handle throws
NoViewerException, another author’s fileNotYourFileException; the UI turns these into messages and offers Fork or Settings. - No Flutter and no plugin in
filmopen_format,filmopen_storeorfilmopen_test_support; no widget package infilmopen_state. - A package imports another only through its barrel (
package:filmopen_x/filmopen_x.dart), neverlib/src/; a new dependency is a line in that package’s pubspec and a row in §3; a boundary that needs a cycle is redrawn, never merged. - Every control an agent drives carries a
Semantics(identifier:)— the regionspage.content,page.tree,page.bodyandpage.header,nav.*,tree:<node id>,tree:<node id>.menuwithtree.add-folder,tree.add-scene,tree.move-up,tree.move-down,tree.move-toandtree.remove,contents.*,move.*,script:<stem>:<n>with its fields,.menuand.handle,script:<stem>:add,script.move*,tree.*,project.*,new-project.*,category.add,new-entity.*,version.*,label.*,file.*,field:<stem>:<key>(the key a field pairs on),epochs.*,banner.fork,compare.copy-*,compare.fork-*,draft.*,guard.*,pick.*,json.*(the JSON editor’s presentation carries its doors under a prefix:json.mode,json.tree,json.text,json.value*in the editor,analysis.review.mode,.tree,.text,.value*in an analysis’s review),media:<stem>:<group>.entry.analyze,analysis.choose.<tag>,analysis.reviewwith.progress,.fields,.failure,.cancel,.update,.fork,.discardand.discard-cancel,analyze:<stem>:<group>(the line under a place),describe.*,settings.*,account.*,sharing.*,activity.*,job:<id>:<action>,keys.*,usage.*,<box>.dictateanddictation.*(§9.4, §9.6, §9.7) — unique on every pagetest/identifiers_test.dartwalks (a version page, the project menu, a category page, three dialogs, the dictation popup, Compare, Settings and its Provider keys tab, Activity’s Usage), and by construction elsewhere (path-like row ids, the file’s stem in a field’s, an enum’s name in a notice’s). Tabs carry none: Material’s tab bar merges each tab’s semantics into one node and an identifier inside breaks that merge; a tab is opened by name (AutomationHost.setTab). - No product widget knows it is recorded; a control an agent drives carries an identifier, and renaming one breaks every tape that names it (§6.8: the issue recorder hears a person through Flutter’s own bindings and the state’s seams, never through a line that says “if recording”).
- Platform code comes in threes:
x.dart(conditional export),x_io.dart,x_stub.dart; the stub answers “not available” (canX = false, null, orUnsupportedError) and the UI hides the affordance. Never importdart:iofrom a file the web build reaches. - Log events, not sentences:
AppLog.instance.info('project.opened', data: {...}); dotted identifiers, structured data, no user-facing text. Log every open, write, failure and setting change; log timings at debug. - Everything is logged as it is; the API key is kept out by construction (the owner, 18 and 21 October 2026): names, tags, stems, paths, folders, prompts, a writer’s source, a plug-in’s lines, the messages to and from a model, its answers, a request and its answer, a server’s text and an exception’s message enter a log line as they are, and no redaction of them is promised. The key the application adds never enters the request’s line, because of where the log is written, and an answer that echoes it is logged as it came: the key box logs no typed value and the key store’s reads log none, and the HTTP door logs a request as the plug-in built it, before the door adds the credential and any medium; the answer is searched for nothing, and the log records it as it came, every header included (the owner, 29 October 2026).
AppLogwrites what it is given (§6.2) — the event’s data at any depth, keys included, its error and its stack, a URL’s user, password and query among them — and no call site cuts; nothing is masked, cut or scanned: a line’s values are the values themselves, never a digest, a count or a length in their place, and a failure carries the error’s type and its message.AppLog.digestOf(value), the first 12 hexadecimal characters of the SHA-256 of a value’s UTF-8, makes an identifier from a value — an operation’s id, a command’s authority — and is never a log value. - A sync or Drive line names a file by its project-relative path as it is, beside its area,
data,mediaorrootby the path’s first segment, and the film by its key, as the application’s own lines do, so a file can be followed across them; a target is named by its identity as it is — a Drive folder’s id, a folder target’s root beside its identity, the Cloud’s<username>/<tag>— never a digest of it. - One rule for short names: anything that must be a segment goes through
ShortNameand, in the UI,ShortNameField. - Never throw on a bad file. Add a
WarningKindand report. - Never choose for the user where the Project Specification says report. Return an unresolved
Resolutionor a null selection. - One way to navigate:
browserProvider.navigate(...). - Comments explain why, and cite the Project Specification section (
§6.1) when a rule comes from it. - Filenames and identifiers follow the format’s vocabulary:
stem,tag,epoch,author,official,take,batch,cue,block. - Tests for every spec rule on an in-memory fixture, not only on the sample.
- Keep
kAppVersioninlib/consts.dartequal toversion:inpubspec.yaml. The constants the packages read (the name, the development-mode switch, the bundled sample) arefilmopen_state/consts.dart. - Commit
pubspec.lock(it is an application) and the generated localisation files.
Media uses the project’s own source. LocalMediaStore.of(project) serves its flat media/ folder. The state provider supplies that MediaStore directly, or null when no project is open. A page that shows media asks ProjectMedia and the store it was given, and never chooses another location for the bytes.
No media file is ever overwritten. A store writes with create, and a name comes from the take grammar with a batch id made unique against the author’s own batches (Project Specification §5.4, §12.3, §18.3). A thumbnail is the exception that proves it: a derivative a reader may re-make, written without create.
15. Third-party code
Section titled “15. Third-party code”packages/filmopen_ui/lib/src/split_view.dart is adapted from API Dash (lib/widgets/splitview_dashboard.dart, Apache License 2.0, revision 8044b218…). The licence is vendored at third_party/apidash/LICENSE, registered with Flutter’s LicenseRegistry in main.dart, and listed in THIRD_PARTY_NOTICES.md. The rail-beside-sidebar arrangement follows API Dash’s dashboard; the theme, model, tree, views and state are FilmOpen code. FilmOpen’s own licence is not yet decided.