Skip to content

Design: per-app launcher descriptors migrate to .deed — the filename is a closed set of four, and this is a rewrite not a rename (standards#960 AC2) #42

Description

@hyperpolymath

⚠ This body was rewritten 2026-09-22 after a citation error. The first version cited deed/spec/abnf/deed.abnf, which is a stale v0.1.0 DRAFT sitting in a peer branch's working tree. The sole normative grammar is 1-formats/deed/spec/abnf/deed.abnf (v1.0.0) on standards origin/main. The conclusion did not change, but two constraints below were missed the first time and both matter. See the comment for the diff.

Context

hyperpolymath/standards#960 AC2 asked whether the 21 per-app launcher descriptors (*.launcher.a2ml) should migrate to .deed or be declared out of scope for D73-C. The owner has ruled: migrate to .deed (2026-09-22, recorded at standards#960).

AC2 is closed by that decision — its wording is "decided, not left to decay". This issue is the design work the decision creates. It is deliberately not a migration PR: it sequences behind #40 (the @a2ml-metadata compat reader), under the dual-accept rule — do not migrate extensions before dual-accept, and a dry-run manifest first, never a bulk pass.

All citations below are to hyperpolymath/standards origin/main.


Question 1 — the filename. <app>.launcher.deed is not a legal deed name.

Deed filenames are a normatively closed production. 1-formats/deed/spec/abnf/deed.abnf:66-71:

deed-filename = estate-file / atlas-file / praxis-file / repo-file
estate-file   = %s"estate_chora.deed"     ; -> estate-deed
atlas-file    = %s"ATLAS.deed"            ; -> estate-atlas-deed
praxis-file   = stem %s"_praxis.deed"     ; -> praxis-deed
repo-file     = stem %s"_chora.deed"      ; -> repo-deed
stem          = 1*( ALPHA / DIGIT / "-" / "." / "_" )

with the matching closed head set at deed.abnf:46 and DEED-GRAMMAR-SPEC.adoc:192, and a semantic constraint at deed.abnf:50-56: "The doc-head MUST match the filename dispatch … A mismatch is a validation error."

Filename dispatch is normative in its own right — the spec says outright that "the grammar alone is not sufficient to dispatch a filename" (DEED-GRAMMAR-SPEC.adoc:310-315). So <app>.launcher.deed, matching none of the four productions, is not a deed.

The stem→head table (DEED-GRAMMAR-SPEC.adoc:276-295) is the authority on meaning:

pattern head meaning
estate_chora.deed (exact) estate-deed Noun. The estate's vocabulary.
*_chora.deed excl. the above repo-deed Noun. What a repo IS. A record.
ATLAS.deed (exact) estate-atlas-deed Noun. The registry of all deeds.
*_praxis.deed praxis-deed Verb. What a tool DOES. Rules.

⭐ Arm B — <app>.launcher_praxis.deed (recommended)

A stem may contain dots. deed.abnf:71 admits . in stem, and deed.abnf:62-64 says so explicitly: "Do NOT split on . — the stem may contain dots (my.project_chora.deed → stem my.project)." (Mirrored at DEED-GRAMMAR-SPEC.adoc:297-299.)

So myapp.launcher_praxis.deed is a legal deed filename, stem myapp.launcher, dispatching to praxis-deed. That matters: the rename is a suffix swap, .launcher.a2ml → .launcher_praxis.deed, and the existing .launcher naming survives intact. No discovery pattern elsewhere in the estate has to learn a new stem shape.

It is also the semantically correct head. A per-app launcher descriptor states what the launcher does for that app — its runtime, its version-output behaviour, its desktop integration. That is a verb, and praxis-deed is the verb form. It composes with what already exists: launcher-standard_praxis.deed states the general rules; <app>.launcher_praxis.deed states that app's conforming praxis. Same head, two scopes.

Arm A — add a fifth head (launcher-deed + its stem pattern)

Honest cost: six sites across three repos, plus an owner ruling — all four existing heads are owner rulings, and praxis-deed was RULED 2026-09-08 on standards#752 as "a genuine fourth head, not a facet of repo-deed."

repo site
standards 1-formats/deed/spec/abnf/deed.abnf:46 (doc-head) and :66 (deed-filename)
standards 1-formats/deed/spec/DEED-GRAMMAR-SPEC.adoc:192 (DocHead) + stem table :276-295 + dispatch side condition :301-308
standards 1-formats/deed/README.adoc (stem/head table)
deed-ecosystem validate-action/validate-a2ml.sh:167 — a regex alternation over the four heads
deed-ecosystem conformance/manifest.a2ml, conformance/run-deed-tests.sh:15, conformance/README.adoc:28
launch-scaffolder crates/launcher-common/src/deed.rs:187 — pub const DOC_HEADS: [&str; 4], a fixed-size array, so a typed change here and a silent one everywhere else

Justified only if a launcher descriptor is genuinely a fifth document form rather than a praxis document. The burden is on arm A to show that.

Arm C — <app>_chora.deed

Semantically wrong, recorded so it is not rediscovered. repo-deed is "What a repo IS — a record", a noun. A launcher descriptor is not a record of what a repo is. It also discards the .launcher infix that arm B keeps for free.


Question 2 — the grammar. A rewrite, not a rename.

The descriptors are TOML-shaped; a deed is not TOML, and the differences are structural:

descriptors use a deed permits
key = value no = as a separator — KEYWORD Sep Value inside s-expressions (= may appear only inside a symbol)
[section] headers no sections — nested ( ... ) clauses; the only bracket is (
tabs tabs are INVALID separators
any UUID uuid5 only (#u5"…"); v4 is deliberately excluded
true / false #t / #f only
TOML escapes exactly four: \" \\ \n \t — \r and \uXXXX are invalid

⚠ The two constraints the first draft of this issue missed

1. :beholding-chora is REQUIRED, and it must be a uuid5. DEED-GRAMMAR-SPEC.adoc:379-414, The praxis-deed form: a praxis deed requires :schema-version, :canonical-name and :beholding-chora. The reason is the one declaration site rule (:398-400, :420-433) — a tool may not declare its own vocabulary, so it must name the chora it reads, "A UUID, never a bare filename — a bare filename resolves against nothing."

So every converted descriptor must name a chora. The good news is that one already serves: launcher-standard_praxis.deed beholds #u5"estate/chora". Whether 21 per-app descriptors should behold the estate chora directly, or a dedicated launcher chora should be declared once and beheld by all of them, is a design decision this issue must make — it is not a detail to settle per-file during a conversion.

2. The praxis field set beyond those three is formally provisional. The spec carries a WARNING: the owner ruled the head but "did not rule on which fields beyond these three a praxis deed must carry — candidates such as :invokes, :emits and :may-refuse are not specified here, because inventing them is precisely the failure this document exists to stop."

Read strictly that blocks arm B, since a launcher descriptor's substance (runtime, version-output, integration) has no specified fields. But there is working precedent in the very file D73-C shipped: launcher-standard_praxis.deed already carries :standard-version, :standard-date and :compliance beyond the required three. So the practical question is not "may a praxis deed carry more fields" — it demonstrably does — but "which fields, named once, for all 21." That is this issue's real substance, and it should be settled as one vocabulary rather than invented 21 times.

⚠ Two versions, not one. :schema-version is the DEED grammar (1.0.0); :standard-version is the document (0.4.0 on the launcher standard today). A converted descriptor carries the first and cites the second. Conflating them makes a stale document read as a newer spec.

⚠ launch-scaffolder has no per-app config deed grammar today. #36 shipped a reader for the standard; there is no schema, reader or writer for a per-app descriptor.


Sequencing

#40 (compat reader, dual-accept)  →  this issue (design)  →  dry-run manifest  →  conversion

Nothing here moves a file. The citation-text fix for these same descriptors is a separate already-ruled workstream (standards#960 AC1): comment-only, independent of the file extension, and must not be folded into this migration.


Acceptance criteria

  1. The filename arm is ruled (A, B or C) with the reason recorded.
  2. The beholding question is ruled: estate chora directly, or a dedicated launcher chora declared once — with the uuid5 named, not left as a filename.
  3. A per-app descriptor field vocabulary is written down once, covering every [section] and key = value in the current descriptors. A TOML key with no deed equivalent is a recorded finding, not a silent drop. Fields are named in one place and reused, never invented per file.
  4. A conformance fixture pair — one valid, one invalid — lands in the deed conformance corpus for the chosen form, and the corpus runner exercises them.
  5. launch-scaffolder reads the new form, with a round-trip test proving a descriptor survives read→write→read byte-identically.
  6. A dry-run manifest lists every file the conversion would touch, current and proposed path, reviewed before any file moves. Both forms are accepted for a stated overlap window before the old one is refused.
  7. If arm A is ruled: all six sites are updated in one change, and the conformance suite fails if any is missed — the [&str; 4] array makes a partial change a compile error in one repo and silent in the others.

Cross-references

🤖 Generated with Claude Code

https://claude.ai/code/session_01WPSJ7fBhVAMcpSffCBWUDo

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:p1High - schedule nextrefactorRestructuring that preserves observable behaviourscope:repoConfined to this repositorystatus:blockedCannot proceed until a dependency clears

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions