DB Reference — qa Domain CLI and Body Write Path

Operator-facing CLI for the QA platform tables and the canonical body write/render pipeline. Cross-link back from db-reference.md for entry points, the domain catalog, table schemas, status lifecycle, and common pitfalls.

qa domain CLI

Python owner: yoke_core.domain.qa. Public command examples use the installed Yoke CLI, which dispatches the registered qa.* function ids:

  • yoke qa requirement add|add-batch|list|get|update|waive ...
  • yoke qa plan materialize --item PREFIX-N --transition T
  • yoke qa plan run --deployment-run-id RUN --stage STAGE [--member PREFIX-N] [--plan PLAN] --project P — --plan only for a stage that names no cases; one already naming its own refuses it
  • yoke qa plan run --deployment-run-id RUN --plan PLAN --project P — run-wide, and refused on a run pinning any QA stage
  • yoke qa run add|complete|record-verdict|list|get ...
  • yoke qa artifact presign|add ...
  • yoke qa gate-summary ...

All CRUD logic for the qa_requirements, qa_runs, and qa_artifacts tables lives in yoke_core.domain.qa; that module name is implementation authority, not an agent-facing command recipe. Full platform documentation: qa-platform.md.

# Add an item-bound review requirement
yoke qa requirement add \
 --item PREFIX-N --qa-kind implementation_review --qa-phase verification \
 --workflow-transition reviewed-implementation

# Bind a method case to a stage in the item's pinned workflow
yoke qa requirement add \
 --item PREFIX-N --method-id browser-check --qa-phase verification \
 --workflow-transition reviewing-implementation \
 --instructions "Inspect the workflow view." \
 --expected-outcome "The workflow view is correct." \
 --method-config '{"steps":[{"action":"navigate","route":"/workflows"}]}'

# Attach an executable case to a deployment run, optionally scoped to one
# stage and one member item the run carries. No workflow transition: the
# run owns that context. Refused on a finished run.
yoke qa requirement add \
 --deployment-run run-YYYYMMDD-NNN --method-id browser-inspection \
 --qa-phase post_deploy --deployment-stage stage-smoke \
 --deployment-member-item PREFIX-N \
 --instructions "Open the released home route." \
 --expected-outcome "The home page renders the new build." \
 --method-config '{"steps":[{"action":"navigate","route":"/"}]}'

# List requirements
yoke qa requirement list --item PREFIX-N

# Get a single requirement
yoke qa requirement get --requirement-id 1

# Update a mutable field on an existing requirement
yoke qa requirement update --requirement-id 4309 --field blocking_mode --value non_blocking
yoke qa requirement update --requirement-id 4309 --field success_policy --value "$policy_json"
yoke qa requirement update --requirement-id 4309 --field method_config --value "$config_json"

# Record a QA run for that item-bound review requirement.
# Blocking passes stamp verification_tree.head_sha from the claimed lane HEAD
# from the clean lane. --raw-result is evidence text, not the run identity.
yoke qa run add \
 --requirement-id 1 --performed-by agent --qa-kind implementation_review --verdict pass

# Epic-task review verdicts use the workflow-item helper path
yoke workflow-item epic-task review-insert \
 --epic PREFIX-833 --task-num 5 --verdict pass --body "Review passed"

# List runs
yoke qa run list --requirement-id 1

# Materialize and execute a named plan against a real deployment run.
# Name the stage the run is at, plus the member when that stage's scope is
# item: a stage credits only requirements bound to both.
yoke qa plan run \
 --deployment-run-id run-YYYYMMDD-NNN --stage item-qa --member PREFIX-N \
 --plan installer-campaign --project P

# Attach an artifact
yoke qa artifact add \
 --requirement-id 1 --run-id 1 --artifact-type screenshot \
 --artifact-handle '{"backend":"local","path":"/tmp/img.png"}'

# Attach inline screenshot bytes (mutually exclusive with --artifact-handle)
yoke qa artifact add \
 --requirement-id 1 --run-id 1 --artifact-type screenshot \
 --content-type image/png --filename capture.png --content-file PATH

# Preview blocking QA gaps before a reviewed-implementation transition
yoke qa gate-summary --item PREFIX-N --target reviewed-implementation --json
Subcommand Args Description
yoke qa requirement add `--item PREFIX-N (--qa-kind K \ --method-id M) --qa-phase P --workflow-transition STAGE [opts], or --deployment-run RUN --method-id M --qa-phase P [--deployment-stage STAGE [--deployment-member-item PREFIX-N]] [opts]` Insert one requirement — bound to a QA-gated stage in the item's pinned workflow, or attached to a deployment run (authorized by the run's project scope, no workflow transition, refused on a finished run or a stage/member the run does not declare)
yoke qa requirement add-batch `--item PREFIX-N (--rows-file PATH \ --stdin)` Insert item-attached requirements atomically; every row requires workflow_transition_id
yoke qa plan materialize --item PREFIX-N --transition T, legacy --deployment-run-id RUN --plan PLAN --project P, or scoped --deployment-run-id RUN --stage STAGE [--member PREFIX-N] [--plan PLAN] --project P Materialize attached item plans, one run-wide plan (refused when the run pins any QA stage, since every stage counts only rows carrying its own name), or frozen stage/member obligations; each case stays on the shared QA authority. For a stage already naming admitted cases, a correction-only --plan is accepted when repeatable --replaces CASE_KEY=FAILED_REQUIREMENT_ID names every plan case. Materialization and declaration commit together: the failed case keeps blocking, leaves the roster, and is superseded when the corrected case passes independent review
yoke qa plan run The same item, legacy run, or scoped stage/member selectors as materialize Execute one server-issued durable roster against its exact subject and target; a run subject also accepts --replaces for the cases its --plan materializes
yoke qa requirement supersede --requirement-id N --superseded-by-requirement-id N --rationale TEXT Discharge a failed case with a passing same-scope corrected case, or retire a post_deploy item source in favor of a corrected item requirement once a passing run case superseded one of its admitted copies; later releases admit only the corrected body
yoke qa requirement supersede --declare-replacement --requirement-id FAILED_ID --superseded-by-requirement-id CORRECTED_ID --rationale TEXT Declare an existing corrected case (a direct run case, or an item case of the same item, transition and phase) as the replacement of a failed one before it passes; the scoped plan runner skips the old capture, grades the corrected blocking case, and supersedes the failure only after its pass.
yoke qa requirement list `[--item PREFIX-N \ --epic PREFIX-N \ --deployment-run-id ID]` List requirements
yoke qa requirement get --requirement-id N Get one requirement
yoke qa requirement update `--requirement-id N --field FIELD (--value VALUE \ --null)` Update one mutable field
yoke qa requirement waive `--requirement-id N --rationale TEXT [--source operator\ agent] [--force]` Waive a requirement with a recorded rationale
yoke qa run add --requirement-id N --performed-by T [--qa-kind K] [--verdict V] [opts] Insert a run; blocking passes are commit-bound
yoke qa run complete `--requirement-id N --run-id N [--verdict V] [--execution-status captured\ capture_failed] [opts]` Complete a previously recorded run
yoke qa run record-verdict --requirement-id N --performed-by T --verdict V [opts] Record a one-shot verdict
yoke qa run list [--requirement-id N] List runs
yoke qa run get --run-id N [--project P] Get one run
yoke qa artifact presign --requirement-id N --run-id N --filename NAME [--content-type CT] Mint a durable upload target
yoke qa artifact add `--requirement-id N --run-id N --artifact-type T (--artifact-handle JSON \ --content-base64 B64 --filename NAME \ --content-file PATH) [opts]` Insert an artifact row from a typed handle or inline bytes
yoke qa gate-summary `(--item PREFIX-N \ --epic PREFIX-N --task-num K) --target reviewed-implementation\ implemented` Read blocking QA gaps for a transition

No public QA init or artifact-list adapter is registered. Schema initialization belongs to DB setup/migrations. Artifact-list remains an implementation/domain capability until a public adapter is registered; do not teach a fake public listing command for it. yoke qa requirement waive and yoke qa run get are registered public adapters.

When to use which mutator. requirement-update changes the policy or executable config of an existing requirement — tighten a success policy, move a requirement between blocking and non_blocking, bind it to a different target_env, correct a live method_config capture script (selectors, waits, assertions), or clear a nullable field. It preserves the requirement identity, so linked runs and artifacts stay attached. A method_config correction invalidates prior greens for gating unless a later run recorded the new config at start; re-run the case. Frozen deployment-run requirements refuse method_config updates. Use requirement-add when the verification surface itself needs to change — for example, swapping from unit_test to integration — since that is a different requirement. requirement-update refuses to mutate qa_kind for exactly that reason. It cannot change instructions or expected_outcome either: a post_deploy item source worded wrong is corrected by adding the corrected requirement and retiring the source with yoke qa requirement supersede after a passing run case answered its failed admitted copy. Recording a requirement never checks the runtime against --target-env; only executing the case does.

Exit codes: 0 = success, 1 = error/not found, 2 = usage error

Body Write Path

items.body is a virtual rendered field. Not stored in the DB. Read via items get PREFIX-N body, which renders on demand from structured fields via render_body.py. Raw body writes were removed. All content must go through structured field writes.

Structured field writes (the only supported path)

All body content reaches the database through structured field writes. The agent path is the Yoke function-call surface (items.structured_field.replace, items.structured_field.append_addendum, items.structured_field.section_upsert, items.structured_field.section_append); see functions.md. Operator/debug callers use the matching CLI adapter:

Agent calls items.structured_field.replace via POST /v1/functions/call (or the in-process dispatcher)
 |
 v   (the operator/debug CLI adapter shown below dispatches the same function id)
yoke items structured-field replace <id> --field <structured-field> --stdin
 |
 v
execute_structured_write() writes the structured field to DB
 |
 +---> GitHub sync (options.sync_github_body)

Reading items get PREFIX-N body renders on demand from all structured fields. When you already have a real artifact file, the same command can read from a body file instead of stdin.

Valid structured fields are spec, design_spec, technical_plan, worktree_plan, shepherd_log, shepherd_caveats, test_results, and deploy_log.

Shepherd subagents (PM, Architect) write structured content during lifecycle transitions (e.g., spec, technical plan, shepherd log/caveats) using the same path.

Error Propagation

Step Owner Error Handling Silent Failure Risk
DB write items.structured_field.* through the Yoke function-call dispatcher Python prints to stderr and exits nonzero Low — stderr propagates to caller
GitHub sync yoke_core.domain.backlog_github_sync helpers Returns nonzero on failure; sync failures are recorded in DB when invoked from backlog mutations Low — failure is tracked and visible

Project-Aware GitHub Sync

GitHub sync operations route through the Python-owned yoke_core.domain.backlog_github_sync helper family and service-client entrypoints. Repo and credential resolution flow through the canonical project-auth helper at yoke_core.domain.project_github_auth.resolve_project_github_auth, which returns the verified binding's owner/repo and a short-lived bearer token.

One GitHub contract for every project. Every registered project resolves through the same surface; no project slug is a silent special case for ambient repo/auth. Project GitHub automation uses a verified GitHub App repo binding plus a short-lived App token. projects.github_repo is only a compatibility display projection; legacy project-secret rows are not a live GitHub auth storage shape.

There is no separate fallback chain: per-project token env-var lookup, project token-file lookup, and silent host-credential fallback are not part of the resolution path.

Fail-closed on missing or broken auth. When the resolver cannot produce a usable repo + App token (missing binding, missing installation, removed repo access, missing permission, private-key/config failure, or GitHub REST transport failure), it raises a typed diagnostic with a concrete repair_command_hint. Yoke-owned callers surface the diagnostic instead of silently falling through to host credentials or treating the remote as empty. Operators repair by connecting GitHub, adding repository access, binding the project repo, or switching the project to disabled. The runtime never instructs the operator to authenticate at the host CLI level as the answer to a project-auth/config problem. GitHub itself failing while the installation token is minted — a 5xx, a rate limit, a network failure or timeout — is not a project-auth problem: it raises github_unavailable (handlers report that code instead of project_auth_error) with the HTTP status in the message and a retry recovery, and retrying callers (workflow dispatch, CI watches, landing readbacks) treat it as transient. A GitHub refusal of the App or its installation stays token_mint_failed, naming the status and the repair it implies: 401 the App JWT (issuer, private key, clock), 403 the installation's access, 404 a missing installation, 422 the requested repository or permissions.

Cross-project coverage. Item sync, status comments, issue close/reopen, body/title sync, resync repair, and doctor GitHub checks all route through the canonical resolver. Resync iterates per project; each iteration calls resolve_project_github_auth(project) and uses bearer-token REST calls for that project. Doctor's GitHub orphan and wrong-repo checks validate label coverage and confirm that each item's GitHub issue exists in the correct repo for its project.

Per-project sync switch precedes auth. A project with projects.github_sync_mode='disabled' never reaches the resolver: every sync helper skips it with one mode-language log line (return code 0, flows continue), and resync excludes it from fetch/classification/repair. The skip is policy, not an auth failure — a disabled project needs no GitHub authorization. See github-sync.md.

GitHub issue body size limit

GitHub rejects issue bodies above ~65,536 characters. Before calling GitHub's issue-body update endpoint, items sync-body and the shared body-update helper measure the rendered Yoke body against a conservative threshold (~62KB) defined in yoke_core.domain.backlog_github_body_budget. When the rendered body fits, full-body sync proceeds normally. When it exceeds the threshold, the helpers route to the compact mirror path instead.

Compact mirror contents. The compact mirror is the substitute body written to GitHub when the full rendered body is over budget. It contains item title, PREFIX-N, project, status, lifecycle state, an explicit note that the Yoke DB holds the canonical full body, key commands/links, and the latest evidence summary. The Yoke DB retains the full body — nothing is lost; the mirror just replaces what gets pushed to GitHub.

Degradation reporting. Compact-mirror sync is reported by the structured-write side-effect surface as degraded_body_budget, distinct from a successful full-body sync and distinct from an auth/config failure. Auth/config failures take precedence: if resolve_project_github_auth fails, no GitHub call is made and the surface reports the auth/config diagnostic, not a body-budget degradation.

Unified across paths. Issue create/reuse, lifecycle transitions, and structured-field side-effect syncs all use the same full-body vs compact-mirror contract — no path-specific drift.

Backfill command. Oversized-body backfill is an operator-maintenance repair for items already linked to GitHub issues whose rendered bodies exceed the budget. The repair identifies oversized rows, resyncs each one through the compact-mirror path, and is rerunnable on partial failure — previously-completed items are no-ops because they already match the compact-mirror state. It does not emit noisy comment chunking; one compact body per linked issue.

Canonical Write Pattern

All agents should use structured field writes. Do not call lower-level item helpers directly, do not edit .md files and hope the content propagates. Raw body writes are unsupported.

DB Reference — qa Domain CLI and Body Write Path

DB Reference — qa Domain CLI and Body Write Path · Yoke