Scriptorium is a WordPress-style CMS written entirely in WFL, rendering
through the Scribe template engine (a git submodule at lib/scribe) and
persisting to SQLite. Start with README.md, then
docs/ARCHITECTURE.md.
docs/PROJECT-LAYOUT.md is the house standard for
NEW WFL projects. This repository does not follow it, and that is deliberate.
Scriptorium predates the policy. Its main.wfl is one 51 KB file, its themes use
sections/ + templates/, and its tests live in TestPrograms/. All three
violate the standard.
- Do not "fix" this repo to match the policy as a drive-by. Retrofitting it is a separate, deliberate migration that has not been approved.
- Do apply the policy in full when scaffolding a new project, or when asked what shape something new should take.
- If a change here would move the repo toward the standard anyway, say so and let Brad decide — don't fold it silently into unrelated work.
- Read
docs/ARCHITECTURE.mdbefore changingmain.wflorapp/. It catalogues the WFL constraints that shaped the design. The structure looks odd until you know which limitation forced it. Most importantly: includes form a tree, not a flat namespace — diamonds break. The library chainutil ← db ← auth ← renderis load-bearing, and the router plus every handler live inmain.wflbecause they must share one scope. - Reserved words.
store,count,data,content,status,header,file,port,error,find,oneand friends are WFL keywords. Qualify identifiers instead:the_status,media_row,db_path. - Run from the repo root. Template and asset paths resolve relative to the working directory.
- Scribe is a submodule. Don't edit
lib/scribe/in place; changes go upstream to WebFirstLanguage/Scribe, then bump viascripts/update-scribe.sh. - Tests:
wfl --test TestPrograms/<name>.test.wfl. There is no test workflow in CI today — the only workflow isupdate-scribe.yml. data_diris an application convention, not a WFL runtime feature.main.wflreads.wflcfgitself at boot and parses the key viaconfig_value_frominapp/util.wfl. The runtime ignores it.
- Theme selection is configurable now.
render_publicresolves<theme_root>/<theme>/body/<name>.html, then.../templates/<name>.html, then the base theme, wherethemeandtheme_rootcome from.wflcfg.main.wflapplies them at boot viaset_public_theme; a module-levelstoreis used becausemain.wflcannot assign to a variable defined in an included file, only call an action that does. This closes the gapdocs/PROJECT-LAYOUT.md§6.1 describes — a site with a custom theme no longer needs a patched clone. Unset keys keep the exact legacy behaviour. app/site_ext.wflis the site-extension seam, and it is whymain.wflincludes it rather thanrender.wfl. A deployment replaces that one file to add its own routes, tables and boot work; the stock copy is inert. It has to be a whole file at the tail of the chain because includes form a tree — a sibling include cannot seerender.wfl's definitions at all. It is consulted first indispatch_public, so a site can own/and still inherit/post/:slug,/page/:slugand the 404. See the header comment in the file, andwebsite/in LogbieLLC/logbie for a real one.- Body template names are a contract.
home.html,post.html,page.html, andnotfound.htmlare named as string literals inside the handlers inmain.wfl. Adding a new body template requires a new handler.
Live Scriptorium sites (news.starnet and others) are Starnet infrastructure.
Follow the workspace instructions in the starnet folder for those: load the
starnet-devops and knowledge-mcp-dev skills, check the knowledge base before
acting, and record what changed afterward. Use git-safe-commit for any git
write operation.