owhile
Profile-driven content gates. It checks whether a body of content is safe for the audience you declared it for. Every audience rule lives in a JSON profile; the gate code never changes. The same code gates a pre-reader safeguarding bank and an adult compliance bank without a line differing — only the profile does.
What this is not. It is not a game engine and not a library of playable mechanics. A mechanic here is a declaration — a verb name, a map of what each field is for, and structural rules — not code you can run. Declaring your own needs no permission and the gates will enforce it, but nothing in this package renders or plays anything. Gating a mechanic and playing one are different problems and only the first is solved.
Built for Owhile, where creators publish interactive experiences.
Install
pip install owhile
owhile --version
The shape of it
Ten play verbs ship with it — swipe, match, sort, build, spot, branch, role-play,
explore-label, reflect, strike-rewrite — each carrying its field roles, structural rules, chance
model and grading. owhile mechanics --library describes them; owhile mechanics add sort puts
one in your profile. You can also declare your own: there is no allowlist in the gate.
The library is at https://github.com/Owhile/owhile-mechanics — readable, forkable, and open to pull requests. The engine that runs them is this package, and is not covered by that grant.
Author and check content:
owhile init # write a starting profile
owhile profile check # is it valid, and could it be listed?
owhile mechanics --library # the ten verbs that ship
owhile mechanics add swipe # take one
owhile mechanics # what your verbs declare, and what they lack
owhile template --mechanic match --out sheet.csv
owhile build --sheet sheet.csv --out bank.ndjson
owhile check --report report.json
owhile docs # the documentation for this version
Then run it in your app — the half that is not a command:
from owhile import profile, grade, session
p = profile.load("profile.json")
grade.grade(p, item, response) # correct? right/of, per decision
s = session.Session.start(p, bank, "learner", seed=user_id)
s.current # what to show
s.respond(answer, item_id=...) # score it and advance
s.state # plain JSON: store it, resume() takes it back
Session is deterministic from its seed and serialisable, so a request-per-answer backend can park
one between calls. Neither draws anything — no components, no CSS, no framework. Presenting the
item is yours, deliberately: in the engine these verbs came from, four of ten kept their difficulty
in pixel thresholds, so a library that draws owns how your product feels.
The runbook, written to be handed to a coding agent, is at https://owhile.vercel.app/creators/start.md.
What it actually does
Three things, and it is worth knowing which is which. It gates content — is this safe to show this audience? It grades a response — was that right? It runs a session — what comes next, and are we done? It does not present anything, and never will.
Five gates run over every item, and each reports pass, fail or could_not_run — so a gate that
could not run is never mistaken for one that passed:
- role_totality — every scalar in an item has a declared role. A field nobody declared is a finding, not a shrug.
- invariants — your structural rules, declared per mechanic and validated at load, so a rule the gate cannot execute is refused before any content is read rather than crashing once per item.
- permissions — an audience may only use the mechanics it permits.
- budgets — reading length, counted in grapheme clusters rather than code points. A cap calibrated on English is a much harsher rule in Devanagari.
- resources — a cited helpline or policy reference must match your registry, and a correct citation is matched at its own full length before any hunt for a wrong one.
The report binds its verdict to a digest of the content and to a digest of the code that produced it, so a verdict cannot outlive either.
Declared, never inferred
The design rule the whole thing turns on. Meaning is declared in your profile and never guessed from prose: which side of a choice is affirming, which text is a myth being struck rather than an assertion, how old your youngest reader is. Earlier code inferred the affirming side by regex-matching an English label and painted the negative side green on sixteen live consent scenarios. A declared field cannot be wrong that way — it can only be absent, and absence blocks.
min_age has no default for the same reason. A profile without one is valid and its content gates
clean; it is listing that is blocked. Silence is never read as adult.
Reporting a problem
https://github.com/Owhile/owhile-web/issues
Do not paste the output of owhile check, and do not attach files. A blocked run prints your
item text verbatim, and a public issue cannot be un-published. Describe it in words — rule names
and counts are fine, finding text is not. Anything that cannot be said in public has a private
route linked from that page.
Status
Honest about where it stops. The gates, the profile loader, the authoring loop and the report
format are built and tested — 217 tests, mutation-tested, and proven across a 29,365-item corpus in
two opposite audiences. The owhile command wrapping them is new in this release and has had far
less use than the code underneath it. Listing on Owhile is not open yet: the eligibility rule and
the digest binding are decided and enforced, and there is no submission path at all.
Known limits are documented rather than hidden, at https://owhile.vercel.app/creators/gates.md.
Licence
The verb library is Apache 2.0. owhile.mechanics — the ten declarations, the kit format and
the conformance check — is yours to take, change and ship, commercially or not, keeping the notice.
A verb kit you contribute comes under the same terms.
Everything else is proprietary. The gates, the profile loader, the forge mechanism, the sheet layer, the grader and the session engine are all rights reserved.
Both texts ship in the wheel: LICENSE and LICENSE-MECHANICS.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file owhile-0.12.0.tar.gz.
File metadata
- Download URL: owhile-0.12.0.tar.gz
- Upload date:
- Size: 104.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44c0447cbfdae78b48b576ce648e7fddaaea3143c8e719ccb18df5ccf3dbf717
|
|
| MD5 |
0dde760670eee7e80b2439d7dbb502bf
|
|
| BLAKE2b-256 |
ffa53f2106203c1158d942bed5196c978b81f707b910229e3e831bd5d45f7183
|
File details
Details for the file owhile-0.12.0-py3-none-any.whl.
File metadata
- Download URL: owhile-0.12.0-py3-none-any.whl
- Upload date:
- Size: 112.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b15dac7c529ce45dd083f85085d002827b19c38f6b2f7eed106cc80bf419928a
|
|
| MD5 |
abe76e085ad4b1c86d4e5e36d3d6229f
|
|
| BLAKE2b-256 |
b01b92ccb2e014d647b5a190018d46924d3d780f8b8484e3487acf6097e426be
|