Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ __pycache__/
# Aider Studio (auto-added)
REPO_MAP.md
.aider/
/.xyz
/.xyz
/SHAKEDOWN
16 changes: 16 additions & 0 deletions .xyz/XYZ.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
[
{
"harness": "relay",
"sessionId": "p1-alpha-merge-qa",
"health": "green",
"title": "RELAY \u00b7 Phase 1 alpha-merge QA \u2014 is `marathon/reflex-hybrid-analytics-starter-p1-2026-07-21` safe to merge to `main`?",
"description": "Relay session ended: STATUS Approved (health green).",
"updatedAt": "2026-07-21T23:29:28Z"
},
{
"harness": "relay",
"sessionId": "p1-alpha-merge-qa",
"health": "red",
"title": "RELAY \u00b7 Phase 1 alpha-merge QA \u2014 is `marathon/reflex-hybrid-analytics-starter-p1-2026-07-21` safe to merge to `main`?",
"description": "Relay session ended: STATUS Open (health red).",
"updatedAt": "2026-07-21T23:27:36Z"
},
{
"harness": "relay",
"sessionId": "p1-alpha-merge-qa",
Expand Down
117 changes: 96 additions & 21 deletions .xyz/utils/pdda/PDDA-INSTALL.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion .xyz/utils/pdda/pdda-catchup.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
set -u
# strict-mode: -e exempt — analysis tool with expected-nonzero probes (git/gh/grep); errors handled explicitly. See GUIDING-PRINCIPLES.md#strict-mode-policy.

HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=utils/pdda/pdda-lib.sh
Expand Down
31 changes: 28 additions & 3 deletions .xyz/utils/pdda/pdda-doc-ready.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
set -u
# strict-mode: -e exempt — analysis tool with expected-nonzero probes (git/gh/grep); errors handled explicitly. See GUIDING-PRINCIPLES.md#strict-mode-policy.

HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=utils/pdda/pdda-lib.sh
Expand Down Expand Up @@ -47,6 +46,8 @@ frontmatter/status-table/hardcoded-path issues (separate deterministic checks ow
- a medium-large plan or project (NOT a typo / path repoint / <=2-3 line fix) whose frontmatter is
missing the triage ratings effort, complexity, risk, phases (used by automation to select work);
do NOT flag genuinely small/trivial docs for this
- a medium-large plan or project where the `related` frontmatter field is empty or missing (suggest they link past context or docs from PROJECT/3-COMPLETED/)
- a plan with frontmatter `risk: 4` or `risk: 5` that does not reference a `decisions/` record anywhere in the doc
- a status table that is present but stale versus the body
- the next action buried in prose instead of stated explicitly
- detail duplicated from another canonical doc
Expand All @@ -58,6 +59,25 @@ for advisory. Do NOT emit "error" — a non-deterministic review must not gain b
If the doc is ready, output nothing at all.
RUBRIC_EOF

# Quad Concepts QUALITY rubric — appended to the working-doc review ONLY when the .pdda-quad / PDDA_QUAD
# lever is on. The deterministic `pdda.sh quad-concepts` check owns STRUCTURE (present, 1-4 bullets); this
# warn-only layer judges what a regex can't: are the bullets real, specific pain->fix concepts, and are
# they still connected to the doc's actual state? It NEVER demands a missing section (that's the
# deterministic check + per-doc quad_exempt), and never blocks.
read -r -d '' QUAD_RUBRIC_APPENDIX <<'QUAD_EOF' || true

ADDITIONALLY — "Quad Concepts" mode is enabled for this repo. If (and only if) the doc has a
"## Quad Concepts" section, review its 1-4 bullets and flag ONLY:
- a bullet that is vague or generic ("backend", "bug", "refactor", "cleanup", "misc") instead of a
specific problem this doc addresses
- a bullet that is not a real "pain -> fix" pairing — it names a topic but not BOTH the problem and how
this doc addresses it
- Quad Concepts that appear disconnected from the doc's "## Status" or "## Lessons Learned": they
describe pains the doc no longer addresses, or omit the main thing the doc is actually about (stale)
Do NOT flag a MISSING "## Quad Concepts" section — a deterministic check and the per-doc `quad_exempt`
frontmatter own presence. These are advisory "warn"/"info" only, never "error".
QUAD_EOF

# ROADMAP.md pointer-only contract (PDDA.md "ROADMAP.md contract"). Honors the deliberate carve-out
# ("a short exception note is allowed when omitting would hide an operationally critical fact"), which
# is exactly why this is judged by the LLM layer rather than a brittle deterministic lint.
Expand Down Expand Up @@ -119,9 +139,14 @@ $response
RESPONSE
}

# 1) active working docs — generic readiness rubric.
# 1) active working docs — generic readiness rubric (+ the Quad Concepts quality rubric when enabled).
WORKING_RUBRIC="$RUBRIC"
if quad_is_enabled; then
WORKING_RUBRIC="$RUBRIC
$QUAD_RUBRIC_APPENDIX"
fi
while IFS= read -r file; do
review_one "$file" "$RUBRIC"
review_one "$file" "$WORKING_RUBRIC"
done < <(pdda_list_working_docs)

# 2) ROADMAP.md — pointer-only contract (separate rubric; skipped if absent).
Expand Down
1 change: 0 additions & 1 deletion .xyz/utils/pdda/pdda-edit-doc-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# WARN-ONLY and FAIL-OPEN: it ALWAYS exits 0, so it can NEVER block the edit. Findings print to stderr
# for visibility only. Wire it in .claude/settings.json (PostToolUse, matcher "Edit|Write|MultiEdit").
set -u
# strict-mode: -e exempt — WARN-ONLY fail-open hook; must never abort mid-scan. See GUIDING-PRINCIPLES.md#strict-mode-policy.

HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=utils/pdda/pdda-lib.sh
Expand Down
17 changes: 4 additions & 13 deletions .xyz/utils/pdda/pdda-gh-refresh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# suite, so `issue-doc-sync` and the Stop scan read fresh state. See PROJECT/PDDA.md "Suggested hourly
# schedule". One-off: `utils/pdda/pdda-gh-refresh.sh`.
set -u
# strict-mode: -e exempt — expected-nonzero gh probes handled explicitly (leaves cache intact on failure). See GUIDING-PRINCIPLES.md#strict-mode-policy.

HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=utils/pdda/pdda-lib.sh
Expand All @@ -32,18 +31,10 @@ main() {
return 4
fi

local tmp count
tmp="$PDDA_GH_STATE_CACHE.tmp.$$"
{
printf '# pdda gh-issue-state cache — regenerated by pdda-gh-refresh.sh; do not edit by hand\n'
printf '# generated: %s\n' "$(pdda_now_iso)"
printf '# format: <issue-number>\\t<STATE> (STATE is OPEN or CLOSED)\n'
[ -n "$table" ] && printf '%s\n' "$table"
} > "$tmp" || { rm -f "$tmp"; printf 'pdda-gh-refresh: write to %s failed\n' "$tmp" >&2; return 5; }

if ! mv "$tmp" "$PDDA_GH_STATE_CACHE"; then
rm -f "$tmp"
printf 'pdda-gh-refresh: could not move cache into place\n' >&2
# One definition of the cache format + atomic write, shared with the live-lookup path in pdda.sh.
local count
if ! pdda_write_gh_state_cache "$table"; then
printf 'pdda-gh-refresh: could not write cache to %s\n' "$(pdda_relpath "$PDDA_GH_STATE_CACHE")" >&2
return 5
fi

Expand Down
141 changes: 125 additions & 16 deletions .xyz/utils/pdda/pdda-lib.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
set -u
# strict-mode: -e exempt — analysis tool with expected-nonzero probes (git/gh/grep); errors handled explicitly. See GUIDING-PRINCIPLES.md#strict-mode-policy.

PDDA_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Runtime lives in <repo>/utils/pdda/, so the repo root is two levels up from the lib dir.
Expand All @@ -9,12 +8,14 @@ PDDA_INBOX_DIR="${PDDA_INBOX_DIR:-$PDDA_REPO_ROOT/PROJECT/1-INBOX}"
PDDA_WORKING_DIR="${PDDA_WORKING_DIR:-$PDDA_REPO_ROOT/PROJECT/2-WORKING}"
PDDA_COMPLETED_DIR="${PDDA_COMPLETED_DIR:-$PDDA_REPO_ROOT/PROJECT/3-COMPLETED}"
PDDA_MISC_DIR="${PDDA_MISC_DIR:-$PDDA_REPO_ROOT/PROJECT/4-MISC}"
# Forward-looking release-planning ledger — a single root file (like ROADMAP.md/CHANGELOG.md), not
# a lifecycle bucket of per-tag docs. See PROJECT/PDDA.md "RELEASES.md — release ledger".
PDDA_RELEASES_FILE="${PDDA_RELEASES_FILE:-$PDDA_REPO_ROOT/RELEASES.md}"
PDDA_ACTIVITY_LOG="${PDDA_ACTIVITY_LOG:-$PDDA_REPO_ROOT/PROJECT/PDDA-ACTIVITY.jsonl}"
# Cached GitHub issue-state file (TSV: "<number>\t<STATE>", '#'-comment lines ignored). Written by
# pdda-gh-refresh.sh; read by `pdda.sh issue-doc-sync` when gh is absent/offline. Gitignored runtime
# state, regenerated on demand — sits beside .pdda-mode at the repo root by default.
PDDA_GH_STATE_CACHE="${PDDA_GH_STATE_CACHE:-$PDDA_REPO_ROOT/.pdda-gh-state.tsv}"
PDDA_RELEASES_FILE="${PDDA_RELEASES_FILE:-$PDDA_REPO_ROOT/RELEASES.md}"
PDDA_STALE_DAYS="${PDDA_STALE_DAYS:-4}"
PDDA_DRY_RUN="${PDDA_DRY_RUN:-0}"
# Output format for findings on stdout: "text" (human, default) or "json" (one JSON object per line,
Expand Down Expand Up @@ -57,6 +58,21 @@ ERROR_COUNT=0
WARN_COUNT=0
INFO_COUNT=0

# Cross-check totals for one `pdda.sh run`, accumulated by pdda_emit_summary. The per-check counters
# above are reset by every pdda_reset_counts, and a check's RETURN VALUE is gated to 0 outside full
# mode — so in observe/light a run of nothing but errors leaves cmd_run's EXIT_CODE at 0. Inferring
# "all checks passed" from that zero is BUG-001b: the mode gate is supposed to stop the run from
# BLOCKING, not to stop it from REPORTING. These totals survive the resets and ignore the gate, so the
# closing line can say what was actually found. Same family as GH-23 and GH-27: a check that could not
# run, or could not block, must never be scored as a check that passed.
PDDA_RUN_ERRORS=0
PDDA_RUN_WARNS=0
PDDA_RUN_ERROR_CHECKS=""
# GH-43: the warn total was accumulated below but never read, so a run whose findings were all
# warn-level printed "all checks passed". Same family, one step further out: PDDA_RUN_ERRORS answers
# "did anything go wrong", not "did anything need attention" — which is what that line asserts.
PDDA_RUN_WARN_CHECKS=""

pdda_now_iso() {
date -u +"%Y-%m-%dT%H:%M:%SZ"
}
Expand Down Expand Up @@ -158,6 +174,17 @@ pdda_emit_summary() {
local summary

summary="errors=$ERROR_COUNT warns=$WARN_COUNT info=$INFO_COUNT"

# Roll this check's findings into the run-level totals before the next pdda_reset_counts wipes them.
PDDA_RUN_ERRORS=$((PDDA_RUN_ERRORS + ERROR_COUNT))
PDDA_RUN_WARNS=$((PDDA_RUN_WARNS + WARN_COUNT))
if [ "$ERROR_COUNT" -gt 0 ]; then
PDDA_RUN_ERROR_CHECKS="$PDDA_RUN_ERROR_CHECKS $check"
fi
if [ "$WARN_COUNT" -gt 0 ]; then
PDDA_RUN_WARN_CHECKS="$PDDA_RUN_WARN_CHECKS $check"
fi

if [ "$PDDA_FORMAT" = "json" ]; then
pdda_json_line "$( [ "$exit_code" -eq 0 ] && printf 'info' || printf 'error' )" \
"$check" "$PDDA_REPO_ROOT" 0 "$summary" "summary"
Expand Down Expand Up @@ -186,10 +213,10 @@ pdda_list_working_docs() {
find "$PDDA_WORKING_DIR" -type f -name '*.md' ! -name 'blank.md' | LC_ALL=C sort
}

# Mirrors pdda_list_working_docs() exactly, just pointed at PDDA_COMPLETED_DIR (PROJECT/3-COMPLETED)
# instead of PDDA_WORKING_DIR. Added for GH-189: check_issue_doc_sync() needs to scan docs that were
# already swept to 3-COMPLETED, since a doc leaves PDDA_WORKING_DIR's scan set the moment the sync
# check's own recommended `git mv` remediation is followed.
# Completed plans (3-COMPLETED). issue-doc-sync needs these: a doc that reached 3-COMPLETED is the
# operator's own assertion that the work is done, so a still-OPEN issue behind it is drift. Without
# this list the check stops watching a doc at the exact moment it completes — and the `git mv` the
# check itself recommends is what blinds it (GH-27).
pdda_list_completed_docs() {
if [ -n "${PDDA_ONLY_FILE:-}" ]; then
case "$PDDA_ONLY_FILE" in
Expand All @@ -210,6 +237,59 @@ pdda_list_inbox_issue_docs() {
find "$PDDA_INBOX_DIR" -type f -name 'GH-*.md' ! -name 'blank.md' | LC_ALL=C sort
}

# Quad Concepts scope: active plans (2-WORKING), issue intake (1-INBOX/GH-*), and archived plans
# (3-COMPLETED — kept glanceable for cold-start recall, per the project-memory-layer reframing).
# Excludes 4-MISC. Honors PDDA_ONLY_FILE (the single-file lint path) exactly like the lists above.
pdda_list_quad_docs() {
if [ -n "${PDDA_ONLY_FILE:-}" ]; then
case "$PDDA_ONLY_FILE" in
*/blank.md) : ;; # scaffold — never in scope (matches the bulk find's blank.md exclusion)
"$PDDA_WORKING_DIR"/*.md|"$PDDA_INBOX_DIR"/GH-*.md|"$PDDA_COMPLETED_DIR"/*.md)
[ -f "$PDDA_ONLY_FILE" ] && printf '%s\n' "$PDDA_ONLY_FILE" ;;
esac
return
fi
{
find "$PDDA_WORKING_DIR" -type f -name '*.md' ! -name 'blank.md' 2>/dev/null
find "$PDDA_INBOX_DIR" -type f -name 'GH-*.md' ! -name 'blank.md' 2>/dev/null
find "$PDDA_COMPLETED_DIR" -type f -name '*.md' ! -name 'blank.md' 2>/dev/null
} | LC_ALL=C sort
}

# Quad Concepts opt-in lever — ORTHOGONAL to PDDA_MODE (observe/light/full). The lever decides whether
# the quad-concepts check joins `pdda.sh run`; the mode still decides report-only vs blocking. Resolution
# mirrors the .sentinel-mode resolver: env PDDA_QUAD -> first non-comment line of <repo>/.pdda-quad ->
# default OFF. Any of 1/on/true/enabled/yes (case-insensitive) enables; everything else stays off.
quad_is_enabled() {
local v="${PDDA_QUAD:-}"
if [ -z "$v" ] && [ -f "$PDDA_REPO_ROOT/.pdda-quad" ]; then
v="$(awk 'NF && $0 !~ /^[[:space:]]*#/ { gsub(/[[:space:]]/,""); print; exit }' "$PDDA_REPO_ROOT/.pdda-quad" 2>/dev/null)"
fi
case "$(printf '%s' "$v" | tr '[:upper:]' '[:lower:]')" in
1|on|true|enabled|yes) return 0 ;;
*) return 1 ;;
esac
}

# Shared Quad Concepts section parser (used by `pdda.sh quad-concepts` AND `pdda.sh glance` — one parser,
# no drift). Emits the bullet COUNT on line 1 (-1 if the section is absent), then each top-level,
# non-empty bullet's TEXT (marker stripped) on its own line. Boundaries: the FIRST "## Quad Concepts"
# header until the next h1/h2 heading or the first blank line AFTER a bullet; fenced code is skipped;
# CRLF is normalized; indented/nested and empty bullets do not count; only the first section is read.
pdda_quad_section() { # <file>
awk '
{ sub(/\r$/, "") }
done { next }
/^```/ { in_f = !in_f; next }
in_f { next }
!seen && /^##[ \t]+Quad[ \t]+Concepts[ \t]*$/ { in_q = 1; seen = 1; started = 0; next }
in_q && /^#{1,2}[ \t]/ { in_q = 0; done = 1; next }
in_q && started && /^[ \t]*$/ { in_q = 0; done = 1; next }
in_q && /^[-*][ \t]+[^ \t]/ { b = $0; sub(/^[-*][ \t]+/, "", b); bl[++n] = b; started = 1; next }
END { if (!seen) print -1; else print n + 0; for (i = 1; i <= n; i++) print bl[i] }
' "$1"
}

pdda_frontmatter_lines() {
awk '
NR == 1 { sub(/^\357\273\277/, "") } # strip a UTF-8 BOM if present
Expand Down Expand Up @@ -324,32 +404,61 @@ _pdda_gh_state_table() {
fi
}

# Write a "<number>\t<STATE>" table to the gh-state cache, atomically (temp file + mv). ONE definition,
# shared by pdda-gh-refresh.sh (the explicit refresh command) and the `auto` lookup path in
# _pdda_issue_state_table (which fetches this exact table on every `pdda.sh run` and used to throw it
# away — GH-27). Returns non-zero on write failure; callers on the read path ignore that, because a
# failed cache write must never break a check that already has its answer in hand.
pdda_write_gh_state_cache() { # <table>
local table="$1" tmp="$PDDA_GH_STATE_CACHE.tmp.$$"
{
printf '# pdda gh-issue-state cache — regenerated by pdda-gh-refresh.sh or any live `pdda.sh` lookup; do not edit by hand\n'
printf '# generated: %s\n' "$(pdda_now_iso)"
printf '# format: <issue-number>\\t<STATE> (STATE is OPEN or CLOSED)\n'
[ -n "$table" ] && printf '%s\n' "$table"
} > "$tmp" 2>/dev/null || { rm -f "$tmp"; return 1; }
mv "$tmp" "$PDDA_GH_STATE_CACHE" 2>/dev/null || { rm -f "$tmp"; return 1; }
}

# --- RELEASES.md helpers -------------------------------------------------------------------------
# RELEASES.md is a single forward-looking planning ledger (like ROADMAP.md/CHANGELOG.md), not a
# bucket of per-tag docs — see PROJECT/PDDA.md "RELEASES.md — release ledger". Each release is a
# flat "Label: value" block; a block starts at a line matching ^Release: and runs until the next
# such line or EOF (blank lines between blocks are just visual spacing, not parsed).

# List releases as rows of
# <release><US><status><US><target_date><US><codename><US><description><US><gh_url><US><line>
# <release><US><status><US><target_date><US><codename><US><description><US><gh_url><US>
# <front_door><US><shakedown><US><license_file><US><line>
# (US = ASCII unit separator 0x1F, not tab — bash's `read` collapses empty fields around literal
# tabs since tab counts as "IFS whitespace" regardless of IFS's contents, which would silently
# misalign every block with a blank Description/GH_URL, i.e. the common case here). One row per
# block, in file order. Prints nothing (silently) if the file doesn't exist.
#
# `Status:` is free-text (Draft/Working/Shipped/... — whatever an operator writes) and unvalidated
# by design: it's a rough, non-authoritative signal for "what's in progress," not a gated lifecycle
# field. See PROJECT/PDDA.md "RELEASES.md — release ledger".
# field. `Front-door reviewed:`/`Shakedown reviewed:`/`License file:` are optional Yes/No QA-gate
# fields (`pdda.sh releases` warns on a non-Yes/No value). See PROJECT/PDDA.md "RELEASES.md —
# release ledger".
pdda_releases_list() {
local file="$1"
[ -f "$file" ] || return 0
awk '
function flush() {
if (has_release) {
printf "%s\037%s\037%s\037%s\037%s\037%s\037%d\n", release, status, target_date, codename, description, gh_url, release_line
printf "%s\037%s\037%s\037%s\037%s\037%s\037%s\037%s\037%s\037%d\n", release, status, target_date, codename, description, gh_url, front_door, shakedown, license_file, release_line
}
release=""; status=""; target_date=""; codename=""; description=""; gh_url=""; release_line=0; has_release=0
release=""; status=""; target_date=""; codename=""; description=""; gh_url=""
front_door=""; shakedown=""; license_file=""; release_line=0; has_release=0
}
/^Release:/ { flush(); v=$0; sub(/^Release:[[:space:]]*/, "", v); release=v; has_release=1; release_line=NR; next }
/^Status:/ { v=$0; sub(/^Status:[[:space:]]*/, "", v); status=v; next }
/^Target Date:/ { v=$0; sub(/^Target Date:[[:space:]]*/, "", v); target_date=v; next }
/^Codename:/ { v=$0; sub(/^Codename:[[:space:]]*/, "", v); codename=v; next }
/^Description:/ { v=$0; sub(/^Description:[[:space:]]*/, "", v); description=v; next }
/^GH_URL:/ { v=$0; sub(/^GH_URL:[[:space:]]*/, "", v); gh_url=v; next }
/^Release:/ { flush(); v=$0; sub(/^Release:[[:space:]]*/, "", v); release=v; has_release=1; release_line=NR; next }
/^Status:/ { v=$0; sub(/^Status:[[:space:]]*/, "", v); status=v; next }
/^Target Date:/ { v=$0; sub(/^Target Date:[[:space:]]*/, "", v); target_date=v; next }
/^Codename:/ { v=$0; sub(/^Codename:[[:space:]]*/, "", v); codename=v; next }
/^Description:/ { v=$0; sub(/^Description:[[:space:]]*/, "", v); description=v; next }
/^GH_URL:/ { v=$0; sub(/^GH_URL:[[:space:]]*/, "", v); gh_url=v; next }
/^Front-door reviewed:/ { v=$0; sub(/^Front-door reviewed:[[:space:]]*/, "", v); front_door=v; next }
/^Shakedown reviewed:/ { v=$0; sub(/^Shakedown reviewed:[[:space:]]*/, "", v); shakedown=v; next }
/^License file:/ { v=$0; sub(/^License file:[[:space:]]*/, "", v); license_file=v; next }
END { flush() }
' "$file"
}
Loading
Loading