TextShelf

Developers › File formats Beta

WorkspacePacks.json

Status: generated from the web's packs. No version field: the file has no schemaVersion and no kind.

WorkspacePacks.json holds the starting rubric for each kind of shelf that has one, as the web defines it, so that "Literary Contest" means the same criteria in the web app and the Mac app. It lives in the repository at native/Packages/ShelfCore/Sources/ShelfCore/Resources/WorkspacePacks.json and ships inside ShelfCore as a resource.

How it is made

  • The source is WORKSPACE_PACKS in textshelf/submissions/workspace_packs.py. The file is never edited by hand.
  • uv run python manage.py export_workspace_packs writes it. It writes nothing when the file is already current.
  • uv run python manage.py export_workspace_packs --check writes nothing. It fails (exit status 1, "… is out of date; run manage.py export_workspace_packs.") when the file differs from the packs or is missing.
  • The one serializer is packs_as_json(): json.dumps(data, indent=2, ensure_ascii=False) + "\n". Keys are in the order shown below, not sorted. Packs are in WORKSPACE_PACKS order. The file ends in one newline and has no trailing spaces.

How it is checked

  • Web tests in textshelf/submissions/tests/test_workspace_packs.py: test_the_checked_in_file_matches_the_packs compares the file to packs_as_json() byte for byte and fails if the file is missing; test_the_file_passes_the_whitespace_hooks checks the ending and trailing spaces; test_check_fails_on_a_stale_file runs --check against a missing file, then a written one. test_every_pack_is_valid_shelf_data checks that criterion names are unique and that each phrase's kind is one of the five below and its text is 2 to 100 characters.
  • Swift tests in native/Packages/ShelfCore/Tests/ShelfCoreTests/WorkspacePackTests.swift pin every pack's field ids and check the rubric made from each pack.
  • No workflow runs --check. The Django CI workflow skips pull requests that change only files under native/, so a hand edit to the JSON alone is not compared with the packs by CI. The Swift tests catch such an edit only when it changes a field id or the set of packs, or empties a description.

Shape

One JSON object. Each key is a web workspace_type; each value is that kind's pack. Today there are three: literary_contest, grant_program and academic_review. Kinds with no pack (custom, and the web's other kinds) are absent.

Key Type Meaning Read by the Mac
label string The web's name for the kind of program, such as literary contests. no
criteria array The starting criteria, in order. yes, required
criteria[].name string The criterion's name. yes, required
criteria[].description string What the criterion asks. yes, required
criteria[].weight number The web's weight. 1.0 in every pack today. no
criteria[].max_score integer Highest score. 5 in every pack today. yes, required
phrases array of [kind, text] pairs Quick feedback phrases the web seeds a new shelf with. kind is praise, suggestion, question, issue or note. no

The Mac ignores keys it doesn't use. If the file is missing, or anything the Mac reads is missing or of the wrong type in any pack, the whole file fails: every kind then starts with an empty rubric, and debug builds stop with an assertion. A pack under a key the Mac has no kind for is loaded and never used.

Abridged (one criterion and two phrases of the literary pack):

{
  "literary_contest": {
    "label": "literary contests",
    "criteria": [
      {
        "name": "Voice",
        "description": "Language that sounds like this writer and no one else: controlled, specific, and alive on the page, or in the mouth for a play.",
        "weight": 1.0,
        "max_score": 5
      }
    ],
    "phrases": [
      [
        "praise",
        "Distinct voice"
      ],
      [
        "suggestion",
        "Tension drops here"
      ]
    ]
  }
}

What the Mac makes of a pack

A new shelf of a kind with a pack gets this rubric in settings.json (shelf-bundle.md §5.2):

  • one score field per criterion, in order: id from the name (below), label the name, min 1, max the max_score, help the description;
  • then {"id": "notes", "label": "Notes", "type": "text"}.

The field ids are also written as seededFieldIDs. custom gets an empty rubric and never reads the file.

A field id is the criterion name with accents folded and letters lowercased, keeping ASCII letters and digits, with each run of anything else turned into one _, and none at either end. "Craft and finish" becomes craft_and_finish. The ids are stored in shelves, so the Swift tests pin them:

Kind Field ids
literary_contest voice, structure, stakes, craft_and_finish, notes
grant_program need_and_significance, approach, capacity, budget, outcomes_and_evaluation, notes
academic_review originality, significance, methods, evidence_and_conclusions, clarity, notes

A renamed criterion, or a change to this rule, changes only what new shelves get. Saved shelves keep the ids they were written with.

Who reads it

  • ShelfCore's WorkspacePacks, which the Mac app uses for:
  • the rubric of a new shelf (File ▸ New, and the kind menu when it replaces an untouched starting rubric);
  • the rubric of a shelf opened with no settings.json, in the app and in its Quick Look extension;
  • the line of criterion names under each kind in the New Shelf menu;
  • the rubric editor's "Replace with … preset" items, one per kind with a pack.
  • The web reads the file only in its test and the export command. It never reads it at runtime: the deployed image excludes native/ (.dockerignore). The web seeds its own shelves from WORKSPACE_PACKS directly.

Where the code is

  • textshelf/submissions/workspace_packs.py:38-52 (the dataclasses), :211-215 (WORKSPACE_PACKS), :222-246 (packs_as_json).
  • textshelf/submissions/management/commands/export_workspace_packs.py:24-62.
  • textshelf/submissions/tests/test_workspace_packs.py:45-63, :238-268.
  • native/Packages/ShelfCore/Sources/ShelfCore/WorkspacePacks.swift:15-112.
  • native/Packages/ShelfCore/Sources/ShelfCore/Models.swift:174-180 (ShelfSettings.seeded), :209-211 (Rubric.defaultFor).
  • native/Packages/ShelfCore/Sources/ShelfCore/ShelfDocument.swift:184-191 (the fallback when settings.json is missing).
  • native/Packages/ShelfCore/Tests/ShelfCoreTests/WorkspacePackTests.swift:15-26.
  • native/Packages/ShelfCore/Package.swift:15-16 (the resource).
  • native/Packages/ShelfUI/Sources/ShelfUI/NewShelfKindMenu.swift:36-41, native/Packages/ShelfUI/Sources/ShelfUI/RubricEditorView.swift:83-89.
  • .github/workflows/django.yml:4-18; .dockerignore:62.

This specification is licensed under CC BY 4.0; the example files under MIT. Anyone may implement these formats in their own software.


This specification is licensed under CC BY 4.0, and its example and schema files under the MIT licence. Anyone may implement these formats in their own software. CC BY 4.0