01 / apply it
yardr pack apply google-driveyardr carries this pack in its binary; the name is enough. yardr pack check google-drive shows what it would change first. It goes into the workspace itself, and into no repo · depot.
yardr pack apply https://yardr.dev/packs/google-drive/From this site, as the files below.
02 / the files
Every file, as it ships.
# Drive as a pack of kind source: the integration, imported into the
# yard and into no depot. What Drive is here: the declared source
# google-drive, which lists and reads files, shares one file with one
# address, copies a file whole, makes folders and empty Docs and Slides
# for the google-docs pack's sources to fill, and lists a file's
# comments and replies in them. Its credential mints from a gws login
# on this machine, by google-mint, the mint command the pack brings
# beside its declaration: applying the pack sets it, with no command
# typed, and a mint_command the operator sets on a source wins over it.
# One command mints every account, choosing the login by the account it
# is given: the yard's login for the yard's calls, that account's own
# login beside it for a call --as a user. The login, the account, the
# mint and calling as a user are the four google packs' shared setup,
# said once in docs/google.md.
#
# yardr pack apply google-drive
#
# A person applies the packs they want, each by one card or typed. A
# pack of kind source asks no grants at import, so what a crew member
# may call is granted afterwards, by the setup notes: read for the
# reads, each update by its name, plain by the operator and as a user
# by that user alone. The share grant allows any address: the setup
# notes teach it as the operator's to give, and each new address as
# asked on its own card.
name: google-drive
kind: source
setup: SETUP.md
sources:
google-drive:
declaration: google-drive.yaml
# Suggested grants: named sets of the source's calls, printed numbered by
# pack show. Suggesting grants nothing: the operator grants them, and
# terminal pack apply ignores them.
presets:
read:
about: List and read files and comments; change nothing
grants:
google-drive: [read]
make:
about: Read files, make folders and empty Docs and Slides, and reply in comments; share nothing
grants:
google-drive: [read, folder, doc, slides, reply]
everything:
about: Read, make, copy and reply, and share files with any address
grants:
google-drive: [read, folder, doc, slides, copy, share, reply]
# Drive as a declared source: files listed, read and shared with one
# address, folders and empty Google Docs and Slides created for the
# docs and slides sources to fill, a file copied whole (a Doc keeps its
# formatting, for the docs source to replace the text of), and a file's
# comments listed and replied to, resolving with the reply when asked.
# What it never does: no delete, no Drive update verb (no rename, no
# move, no change of content: a copy is a call of its own), no trash,
# no upload of content (multipart, which a declared source cannot say),
# no export (a media download, not JSON), and no paging (pageToken is
# not declared, so one page is all a call reads). A share is reader or
# commenter to one address: no writer or owner, no domain or
# anyone-with-link share, no unsharing. A copy is one file under a new
# name, in one folder when asked: no folder copied. A reply answers a
# comment that is there: no new top-level anchored comment (anchors are
# fragile), no deleting, no reopen, and no hearing comments as events.
#
# Checked against the discovery document
# (https://www.googleapis.com/discovery/v1/apis/drive/v3/rest) on 16
# October 2026: files.list takes q (default pageSize 100) and answers
# one page with files, incompleteSearch and nextPageToken; files.get
# answers the file's metadata; files.create takes {name, mimeType,
# parents} and answers the file. permissions.create checked 17 October
# 2026: it takes {type, role, emailAddress} with sendNotificationEmail
# (Drive's own default true) and answers the permission. comments.list
# and replies.create checked 17 October 2026: comments.list takes the
# fileId with the REQUIRED fields parameter and answers one page with
# comments and nextPageToken, resolved and open alike, each marked by
# its own resolved flag (Drive has no resolved filter, so the call
# takes none); replies.create takes {content, action} and answers the
# reply, resolving the thread when the action is resolve. files.copy
# checked 28 October 2026: it takes the file's id in the path and
# {name, parents} (one parent at most; with none, the copy inherits the
# original's) and answers the file, in the few fields a files method
# answers by default unless fields names more, which the call does for
# the three it keeps.
#
# The credential mints like google-mail's: GOOGLE_ACCOUNT names the
# account (an address, no secret) and google-mint, beside this file,
# mints from it, which the operator's own mint_command wins over. Drive
# takes no idempotency key, so the file names none: a folder or a copy
# tried again after a lost answer is made twice.
name: google-drive
about: Drive files listed, read, copied and shared; folders and empty Docs and Slides created; comments listed and replied to.
base: https://www.googleapis.com
credential:
env: GOOGLE_TOKEN
header: "Authorization: Bearer {credential}"
mint:
- name: GOOGLE_ACCOUNT
secret: false # an address, asked in the clear
mint_command: [google-mint] # the file beside this one; the source's own mint_command wins
check: files
calls:
files:
kind: read
about: List files, one page
params:
q: { type: string, max: 1000, optional: true } # free text, Drive's query syntax; left out, the drive's files
request:
method: GET
path: /drive/v3/files
query: { q: "{q}" }
answer: { select: [files, incompleteSearch, nextPageToken] } # nextPageToken says there is more; paging is left out
file:
kind: read
about: Read one file's metadata
params:
id: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
request:
method: GET
path: /drive/v3/files/{id}
query: { fields: "id,name,mimeType,parents,modifiedTime,size,trashed" } # Drive answers a default few fields unless told which
answer: { select: [id, name, mimeType, parents, modifiedTime, size, trashed] }
folder:
kind: update
about: Make a folder in a parent
params:
name: { type: string, max: 200 } # free text: its value is in no event
parent: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
request:
method: POST
path: /drive/v3/files
query: { fields: "id,name,mimeType,parents" }
body: { name: "{name}", mimeType: "application/vnd.google-apps.folder", parents: ["{parent}"] }
answer: { select: [id, name, mimeType, parents] }
doc:
kind: update
about: Make an empty Google Doc for the docs source to fill
params:
name: { type: string, max: 200 }
parent: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
request:
method: POST
path: /drive/v3/files
query: { fields: "id,name,mimeType,parents" }
body: { name: "{name}", mimeType: "application/vnd.google-apps.document", parents: ["{parent}"] }
answer: { select: [id, name, mimeType, parents] }
slides:
kind: update
about: Make an empty Google Slides deck for the slides source to fill
params:
name: { type: string, max: 200 }
parent: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
request:
method: POST
path: /drive/v3/files
query: { fields: "id,name,mimeType,parents" }
body: { name: "{name}", mimeType: "application/vnd.google-apps.presentation", parents: ["{parent}"] }
answer: { select: [id, name, mimeType, parents] }
copy:
kind: update
about: Copy a file whole, formatting and all, under a new name
params:
id: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
name: { type: string, pattern: '^[^\n]+$', max: 200 } # free text, held to one non-empty line: its value is in no event
parent: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true, optional: true } # left out, the parents list goes empty and names no folder
request:
method: POST
path: /drive/v3/files/{id}/copy
query: { fields: "id,name,webViewLink" } # Drive answers a default few fields unless told which
body: { name: "{name}", parents: ["{parent}"] }
answer: { select: [id, name, webViewLink] }
share:
kind: update
about: Share a file with one address, reader or commenter
params:
fileId: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
email: { type: string, pattern: "^[^@ ]+@[^@ ]+$", max: 254, log: true } # one address; the grant allows any
role: { type: enum, values: [reader, commenter] } # no writer or owner
notify: { type: bool, default: false } # Drive defaults to notifying; the yard defaults to quiet
request:
method: POST
path: /drive/v3/files/{fileId}/permissions
query: { sendNotificationEmail: "{notify}" }
body: { type: "user", role: "{role}", emailAddress: "{email}" }
answer: { select: [id, type, role] } # the permission it made; kind is left out
comments:
kind: read
about: List a file's comments, resolved and open alike, each told by its resolved flag
params:
fileId: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
request:
method: GET
path: /drive/v3/files/{fileId}/comments
query: { fields: "comments(id,author,content,quotedFileContent,resolved,replies),nextPageToken" } # Drive refuses the call without fields
answer: { select: [comments, nextPageToken] } # nextPageToken says there is more; paging is left out
reply:
kind: update
about: Reply to a comment, resolving it with the reply when asked
params:
fileId: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
commentId: { type: string, pattern: "^[A-Za-z0-9_-]+$", max: 200, log: true }
content: { type: string, max: 10000 } # free text: its value is in no event
action: { type: enum, values: [resolve], optional: true } # left out, a plain reply; resolve closes the thread with it
request:
method: POST
path: /drive/v3/files/{fileId}/comments/{commentId}/replies
body: { content: "{content}", action: "{action}" } # an action left out leaves the key, so the reply only answers
answer: { select: [id, content, action] } # the reply it made; action only when it resolved
#!/bin/sh
# Mints a Google access token from a gws login: the mint command the
# google packs' declarations bring (credential.mint_command), whose
# credential names GOOGLE_ACCOUNT under credential.mint. Applying a
# pack copies it under <home>/sources/<source>/; the core runs it with
# {inputs, scope} on stdin and reads {token, expires_at} on stdout.
#
# GOOGLE_ACCOUNT is the account's address, no secret. The refresh
# credentials come from the local gws login for that account: the
# default login in ~/.config/gws where it is for that account, else
# that account's own login in ~/.config/gws-accounts/<account>,
# chosen by this script with
# GOOGLE_WORKSPACE_CLI_CONFIG_DIR for the gws it starts alone. gws auth
# status tells whose login one is, and gws auth export --unmasked prints
# the client id, client secret and refresh token, which are exchanged at
# the token endpoint for an access token. No new copy of a secret is
# stored: the three are held in this process's memory alone, sent in the
# exchange's body, and never printed, neither on stdout nor on stderr,
# nor written anywhere.
#
# Needs curl on PATH and gws: GWS_BIN names another gws, else the one on
# PATH, else the first gws in the common install directories of both
# platforms, the operator's own before the machine's.
# GOOGLE_TOKEN_ENDPOINT may name a loopback HTTP endpoint for a test's
# stand-in. The scope is the call's;
# Google's token narrows by the gws grant, not by call, so it is read
# and ignored.
set -eu
fail() { echo "google-mint: $*" >&2; exit 1; }
command -v curl >/dev/null 2>&1 || fail "needs curl on PATH"
if [ -n "${GWS_BIN:-}" ]; then
case $GWS_BIN in "~/"*) GWS_BIN=$HOME/${GWS_BIN#"~/"} ;; "~") GWS_BIN=$HOME ;; esac
[ -x "$GWS_BIN" ] || fail "GWS_BIN=$GWS_BIN is no program to run"
GWS=$GWS_BIN
elif command -v gws >/dev/null 2>&1; then
GWS=gws
else
# A process started with a short PATH (the daemon's own) still finds
# the gws its operator installed: the directories gws's install
# methods use on either platform, the operator's own before the
# machine's.
GWS=
for dir in "${HOME:-}/.local/bin" "${HOME:-}/.cargo/bin" /opt/homebrew/bin /usr/local/bin; do
if [ -x "$dir/gws" ]; then
GWS=$dir/gws
break
fi
done
[ -n "$GWS" ] || fail "needs gws on PATH or in a common install directory (or GWS_BIN=/path/to/gws): the operator's gws login is what the token is minted from"
fi
input=$(cat)
[ -n "$input" ] || fail "reads {inputs, scope} on stdin, and got nothing"
# first <text> <key> is the value of the first "key": "value" in text, ""
# when there is none. Values with an escaped quote in them are not read:
# an account, a scope, gws's answers and Google's have none. A line gws
# prints outside its answer (Using keyring backend: keyring, on stderr)
# has no quoted pair in it, so it is passed over either way.
first() {
printf '%s' "$1" | grep -o "\"$2\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" |
head -n 1 | sed 's/^[^:]*:[[:space:]]*"//;s/"$//'
}
# num <text> <key> is the number of the first "key": <number> in text, ""
# when there is none.
num() {
printf '%s' "$1" | grep -o "\"$2\"[[:space:]]*:[[:space:]]*[0-9][0-9]*" |
head -n 1 | sed -n 's/[^0-9]*\([0-9][0-9]*\)/\1/p'
}
# shell_quote <text> is text as one shell word: as it is when nothing in
# it is special, single-quoted otherwise. For the login command a
# refusal names: the account that names its directory is data, so a
# ';', '$' or space in it must not become a command when it is copied.
shell_quote() {
case $1 in
*[!A-Za-z0-9/._+:,@%=-]*|'')
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
;;
*)
printf '%s' "$1"
;;
esac
}
account=$(first "$input" GOOGLE_ACCOUNT)
[ -n "$account" ] || fail "its inputs name no GOOGLE_ACCOUNT"
printf '%s' "$input" | grep -q '"scope"' || fail "reads {inputs, scope} on stdin, and got no scope"
endpoint=${GOOGLE_TOKEN_ENDPOINT:-https://oauth2.googleapis.com/token}
if [ "$endpoint" != "https://oauth2.googleapis.com/token" ]; then
# The override is for the test stand-in only. Keep refresh credentials
# from being sent to an endpoint supplied by the process environment.
case $endpoint in
http://127.0.0.1:*)
port=${endpoint#http://127.0.0.1:}
case $port in ''|*[!0-9]*) fail "GOOGLE_TOKEN_ENDPOINT is only allowed for a loopback test endpoint" ;; esac
;;
*) fail "GOOGLE_TOKEN_ENDPOINT is only allowed for a loopback test endpoint" ;;
esac
fi
# Whose login gws holds: the exchange below mints for whoever that is,
# so another account than the inputs name is refused, not minted. The
# default login is tried first, exactly as before; only where it answers
# another account is that account's own login tried, in the directory the
# pack declares beside gws's own.
status_out=$("$GWS" auth status 2>&1) || fail "gws auth status failed (exit $?): $(printf '%s' "$status_out" | head -c 500)"
user=$(first "$status_out" user)
[ -n "$user" ] || fail "gws auth status answered with no user"
if [ "$user" = "$account" ]; then
# The refresh credentials, decrypted by gws: masked without
# --unmasked, so that flag is the point. What fails is said in one
# line: the three values reach neither stdout nor stderr from here on.
export_out=$("$GWS" auth export --unmasked 2>&1) || fail "gws auth export failed (exit $?)"
else
# The account names a directory below, so one that is no single name
# is refused before it names one.
case $account in
*"/"*|"."|"..") fail "GOOGLE_ACCOUNT=$account names no per-account login (~/.config/gws-accounts/<account>)" ;;
esac
[ -n "${HOME:-}" ] || fail "HOME is not set: cannot find the login for GOOGLE_ACCOUNT=$account"
acct_dir=$HOME/.config/gws-accounts/$account
login_cmd="GOOGLE_WORKSPACE_CLI_CONFIG_DIR=$(shell_quote "$acct_dir") gws auth login -s gmail,drive,docs,slides,calendar"
# gws just answered above, so what fails here is the login in that
# directory, not gws: the refusal names the login that makes it, and
# not what gws said.
status_out=$(GOOGLE_WORKSPACE_CLI_CONFIG_DIR=$acct_dir "$GWS" auth status 2>&1) || fail "no gws login for GOOGLE_ACCOUNT=$account in $acct_dir ($login_cmd makes it)"
user=$(first "$status_out" user)
[ -n "$user" ] || fail "no gws login for GOOGLE_ACCOUNT=$account in $acct_dir ($login_cmd makes it)"
[ "$user" = "$account" ] || fail "the gws login in $acct_dir is for $user, not GOOGLE_ACCOUNT=$account ($login_cmd makes it)"
export_out=$(GOOGLE_WORKSPACE_CLI_CONFIG_DIR=$acct_dir "$GWS" auth export --unmasked 2>&1) || fail "gws auth export for GOOGLE_ACCOUNT=$account in $acct_dir failed (exit $?)"
fi
# (What it said is not repeated: on this line the refresh credentials
# themselves would be the leak.)
client_id=$(first "$export_out" client_id)
client_secret=$(first "$export_out" client_secret)
refresh_token=$(first "$export_out" refresh_token)
[ -n "$client_id" ] && [ -n "$client_secret" ] && [ -n "$refresh_token" ] ||
fail "gws auth export answered with no refresh credentials"
# The exchange is one form, sent as it is: what gws exports (letters,
# digits, dots, tildes, slashes and hyphens) needs no encoding, and
# anything else is refused rather than sent half-encoded. The body goes
# over the pipe, not in an argument and not in a file.
case $client_id$client_secret$refresh_token in
*[!A-Za-z0-9._~/-]*)
fail "what gws answered is no refresh credentials" ;;
esac
out=$(printf 'grant_type=refresh_token&client_id=%s&client_secret=%s&refresh_token=%s' \
"$client_id" "$client_secret" "$refresh_token" |
curl -s --max-time 10 -w '\n%{http_code}' -d @- "$endpoint") ||
fail "could not reach $endpoint (curl exit $?)"
code=$(printf '%s' "$out" | tail -n 1)
body=$(printf '%s' "$out" | sed '$d')
# The response body is untrusted and could echo the refresh credentials.
# Report only the status code on failure.
[ "$code" = "200" ] || fail "minting a token was answered $code"
token=$(first "$body" access_token)
expires_in=$(num "$body" expires_in)
[ -n "$token" ] && [ -n "$expires_in" ] || fail "minting a token came back with no token"
# The answer is JSON the core reads: a token outside what Google sends
# is refused rather than printed through it.
case $token in *[!A-Za-z0-9._-]*) fail "the token Google answered is no token" ;; esac
now=$(date +%s)
exp=$(date -u -r "$((now + expires_in))" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null ||
date -u -d "@$((now + expires_in))" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null) ||
fail "cannot write the time (date reads neither -r nor -d here)"
printf '{"token":"%s","expires_at":"%s"}' "$token" "$exp"
# Setting up Drive
The pack wires Drive into the yard as the declared source
`google-drive`: files listed, read and shared with one address, a
file copied whole under a new name, folders made, empty Docs and
Slides made for the google-docs pack's sources to fill, and a file's
comments listed and replied to.
Each call goes out with a short-lived token minted
from a gws login on this machine, so no new copy of a secret is
stored anywhere. The login, the
account, the mint and calling as a user are the four google packs'
shared setup, said once in `yardr docs google`.
1. **The gws login.** It needs the Drive scope,
`https://www.googleapis.com/auth/drive`, else the calls come back
403. The shared login instructions in `yardr docs google` start
from a machine with no gws, and show how to check and add the
`drive` service while keeping the other applied packs' scopes.
2. **The setup card.** One card carries the pack's whole setup: the
pack, the account and the grants. Have a crew member ask for it,
and approve it, in footplate or in `yardr chat` (`ctrl+o`); a
person sets up the packs they want, one card each:
```sh
yardr bead ask <bead> "Set up Drive for the yardmaster?" --setup google-drive --crew yardmaster --recommend 1 --why "reading is the job; making and sharing can follow" --context "The yardmaster lists and reads Drive files to work beads."
```
The card shows the declaration whole with its mint, the account
to type or take (step 3), and one option per preset (step 5).
By hand it is the operator's to type, step by step, starting
with the pack:
```sh
yardr pack apply google-drive
# applied pack google-drive to the yard: 1 created, 0 changed, 0 unchanged
# source google-drive is declared: 9 calls under https://www.googleapis.com (yardr source show google-drive)
```
3. **The account.** The yard's Google account for `google-drive` is
answered on the setup card, as described in `yardr docs google`
(step 3): typed unseen on the first pack, then picked.
4. **The mint.** Nothing to type: the pack brings `google-mint`
beside its declaration, and applying it (step 2) copies it for
`google-drive`, its sha256 on the card. What it needs and refuses,
and how a mint of the operator's own replaces it, are in
`yardr docs google`.
5. **The grants.** A pack of kind source asks none at import, so what
a crew member may call is picked on the setup card as one preset.
The pack's presets, as `yardr pack show google-drive` prints them
numbered:
1. `read`: every read call — files and comments listed and read —
and it covers any read call the source is later given; changes
nothing.
2. `make`: every read call, `folder`, `doc`, `slides` and `reply`:
folders made, empty Docs and Slides made for the google-docs
pack's sources to fill, replies in a file's comments; shares
nothing.
3. `everything`: every read call, `folder`, `doc`, `slides`,
`copy`, `share` and `reply`: what `make` grants, copies of a
whole file, and shares of one file with any address.
A grantee who comes later gets one preset grant card:
```sh
yardr bead ask <bead> "Let the reviewers read Drive" --run "source grant google-drive --preset read --group reviewers" --context "The reviewers list and read Drive files to check a bead's work; the card names each call it covers."
```
By hand they are the operator's to type:
```sh
yardr source grant google-drive read --crew yardmaster
yardr source grant google-drive folder --crew yardmaster
yardr source grant google-drive doc --crew yardmaster
yardr source grant google-drive slides --crew yardmaster
yardr source grant google-drive copy --crew yardmaster
yardr source grant google-drive share --crew yardmaster
yardr source grant google-drive reply --crew yardmaster
```
A copy clones a whole file, text and formatting, which is more
than making an empty one, so `copy` is in the `everything` preset
alone: `make` makes empty files and copies none. To give a crew
member copies without shares, grant `copy` by name, as above.
Sharing sends yard content to a person outside it, so `share` is
granted by the `everything` preset alone, which the operator may
give or withhold: without it a crew member shares nothing. The
grant alone allows any address. The
yardmaster's habit, not a mechanism, is to ask on a card before
each share to a new address, naming the file and the address, and
to leave `notify` out for quiet sharing, or pass `notify=true` when
the person expects mail.
As ada, the same grants with `--as ada`, on cards addressed to
ada and approved by ada alone: the shared card is in
`yardr docs google` ("Calling as a user"), one card per grant, with
`read`, `folder`, `doc`, `slides`, `copy`, `share` and `reply` from
above. There is no typed way beside these: a grant `as` ada is
ada's to give.
`yardr source show google-drive` lists each call with its kind
and who may make it, plain and `as` each user.
6. **Check it.** The drive source's check call lists the drive's
files, minted and live:
```sh
yardr source check google-drive
# google-drive: credential: GOOGLE_ACCOUNT is set in /Users/me/.yardr/sources/google.env, minted, good until 16:59
# google-drive: check call files (GET /drive/v3/files): 200 in 412ms, 1834 bytes
```
7. **Try it.** As the operator, who may make any call: a folder with
an empty doc in it, which the google-docs pack's sources then
fill:
```sh
yardr call google-drive folder name="notes" parent=<a folder id>
yardr call google-drive doc name="lunch" parent=<the folder's id>
```
Or a copy of a file that is there, formatting and all; a copied Doc
is one whose text the google-docs source can then replace. `parent`
puts the copy in a folder; without it the copy inherits any
discoverable parent of the source file, as Drive's copy method
specifies:
```sh
yardr call google-drive copy id=<a file id> name="lunch, again"
```
And a share, which is for real: the address sees the file, quiet by
default unless `notify=true`:
```sh
yardr call google-drive share fileId=<the file's id> email=<an address> role=reader
```
And a file's comments, listed with their replies: resolved and
open alike, each told by its own `resolved` flag, since Drive
has no resolved filter:
```sh
yardr call google-drive comments fileId=<the file's id>
yardr call google-drive reply fileId=<the file's id> commentId=<a comment's id> content="Done."
yardr call google-drive reply fileId=<the file's id> commentId=<a comment's id> content="Fixed." action=resolve
```
The last reply closes the thread with it; without `action`
the reply only answers.
Calling as a user — the yard reading another person's drive with
their own credential — is the shared setup too: `yardr docs google`
has it.
What is not there, on purpose: deleting, trash, updating a file in
place (renaming or moving it); copying folders (a copy is of one
file); uploading file content and attachments (multipart, which a
declared source cannot say); exporting file content (a media download,
not JSON); paging past the first page; writer or owner shares, domain
or anyone-with-link shares, and unsharing; new top-level anchored
comments (anchors are fragile), deleting comments or replies,
reopening, and hearing comments as events; and no call the pack
does not list. The yard's source format cannot express what is not
listed, and the pack does not change yardr.