feat(board): --voyage reports the Voyage briefs in flight
board read STATE lines and knew nothing about a brief, so a programme running Voyage across several repos had no shared surface: nobody could answer which briefs were in flight, in what phase, and who was waiting on whom. Order 20260831T135934Z-696228720 fired the programme-level B1 row once /trekplan had been delivered in llm-ingestion-okf, so the fields were chosen against a run that had gone the whole way rather than guessed. Adds board.sh --voyage (a sixth rendering of the same scan, read-only) and a VOY column beside ORDRE and FLY, never summed with them and never in the sort. Detection is by PROPERTY, never by directory name: a directory holding brief.md or brief.md.draft under any of the three planning locations the convention recognises. It walks the FILESYSTEM, never the git index - llm-ingestion-okf gitignores .claude/projects/ (087be0b), so an index-based detector would report ZERO briefs in the one repo actually running one. The phase ladder measures ARTIFACTS, not sessions, and the legend says so: /trekexecute leaves a file behind only in its multi-session form, so a plan executed in one session leaves nothing and `plan` is the last thing the filesystem can prove. Nothing here claims a session is alive - the same refusal FLY carries. Three absences that must not borrow the shape of a measurement: brief_quality is read from the frontmatter block only and an absent field reads `-`, never `complete` (only 8 of ~40 briefs on the real tree carry it); research=- (never started) is distinct from research=0 (a directory holding nothing); and --voyage always prints its own denominator rather than rendering as an empty page. Blocking decisions are counted at DECLARATION SITES, not mentions: the pattern occurs on four lines of the real brief, of which one declares it, so a bare grep -c answers 4 where the honest answer is 1. The age is the NEWEST artifact, inverting the oldest-wins rule ORDRE and FLY carry - an order queue's problem is the oldest item still waiting, a project's problem is that its most recent activity is old. Mutation-verified three ways: newest->oldest turns exactly one check red, anchored pattern->bare string turns three red (the known-negative control among them), filesystem->git ls-files turns 23 red. Live-verified against the completed run: fase=plan, kvalitet=complete, blokkerende=1, gate=S4, research=5 - the only project of 51 across 14 repos with an open blocking decision. Cost measured rather than assumed: 0.31.0 takes 6.3s over the real 52-repo tree, with VOY 7.7-9.2s. The first cut called stat once per artifact and took 13.4s; the batched form is what makes the column affordable. CLAUDE.md's old "~3s" claim did not survive the measurement and is corrected. Bounded gap, stated rather than closed: whether a detected project is still "in flight" is not decided here. That needs a threshold, and a threshold would make the board decide that work is abandoned - the identical thing the order queue is already forbidden from doing. board-selftest.sh: 325 -> 360 checks. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
0711b4209c
commit
85cd628c3e
5 changed files with 701 additions and 21 deletions
305
scripts/board.sh
305
scripts/board.sh
|
|
@ -207,7 +207,7 @@
|
|||
# see the dispatch() comment for what happens when it is guessed.
|
||||
#
|
||||
# Usage: board.sh [--roots <dir>[,<dir>...]] [--plain]
|
||||
# [--brief|--plan|--inbox-plan|--dispatch ...]
|
||||
# [--brief|--plan|--inbox-plan|--dispatch|--voyage ...]
|
||||
# [--focus "<prose>"]
|
||||
# Env: CLAUDE_COORD_DIR overrides the mailbox root.
|
||||
# BOARD_ROOTS overrides the default scan roots.
|
||||
|
|
@ -223,6 +223,7 @@ PLAN=0
|
|||
INBOX_PLAN=0
|
||||
FOCUS=""
|
||||
DISPATCH=0
|
||||
VOYAGE=0
|
||||
D_REPO=""; D_PROMPT=""; D_PANE=""; D_ORDER=""
|
||||
D_PATH=""; D_VER=""; D_REV=""; D_SCOPE=""; D_RAT=""
|
||||
|
||||
|
|
@ -238,9 +239,9 @@ while [ $# -gt 0 ]; do
|
|||
--roots) [ $# -ge 2 ] || { echo "board: --roots requires a value" >&2; exit 2; }
|
||||
ROOTS="$2"; shift 2 ;;
|
||||
# Four renderings of one scan, so exactly one may be selected: last wins.
|
||||
--brief) BRIEF=1; PLAN=0; INBOX_PLAN=0; shift ;;
|
||||
--plan) PLAN=1; BRIEF=0; INBOX_PLAN=0; shift ;;
|
||||
--inbox-plan) INBOX_PLAN=1; BRIEF=0; PLAN=0; shift ;;
|
||||
--brief) BRIEF=1; PLAN=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--plan) PLAN=1; BRIEF=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--inbox-plan) INBOX_PLAN=1; BRIEF=0; PLAN=0; VOYAGE=0; shift ;;
|
||||
# Raw operator prose, forwarded verbatim by the driver: it does not
|
||||
# tokenize, match or normalize, so every bit of that work is here. Same
|
||||
# `shift 2` guard as --roots, for the same bash 3.2 reason.
|
||||
|
|
@ -248,7 +249,8 @@ while [ $# -gt 0 ]; do
|
|||
FOCUS="$2"; shift 2 ;;
|
||||
# Dispatch rendering: one repo, one prompt file, one startup command. Same
|
||||
# `shift 2` guard as every other value flag, for the same bash 3.2 reason.
|
||||
--dispatch) DISPATCH=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; shift ;;
|
||||
--dispatch) DISPATCH=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; VOYAGE=0; shift ;;
|
||||
--voyage) VOYAGE=1; BRIEF=0; PLAN=0; INBOX_PLAN=0; DISPATCH=0; shift ;;
|
||||
--repo) [ $# -ge 2 ] || { echo "board: --repo requires a value" >&2; exit 2; }
|
||||
D_REPO="$2"; shift 2 ;;
|
||||
--prompt-file) [ $# -ge 2 ] || { echo "board: --prompt-file requires a value" >&2; exit 2; }
|
||||
|
|
@ -403,6 +405,169 @@ oldest_order_age() {
|
|||
printf '%dd' $(( (NOW - ooa_oldest) / 86400 ))
|
||||
}
|
||||
|
||||
|
||||
# --- Voyage project detection (ORDRE 20260831T135934Z-696228720) ------------
|
||||
# board read STATE lines and knew nothing about Voyage briefs, so a programme
|
||||
# running Voyage across several repos had no shared surface: nobody could
|
||||
# answer "which briefs are in flight, in what phase, and who waits on whom".
|
||||
#
|
||||
# Detection is by PROPERTY, never by directory name. The global CLAUDE.md says
|
||||
# plans and briefs are defined by properties and recognises three locations
|
||||
# (docs/, .claude/projects/, features/<n>-<name>/), so a directory counts as a
|
||||
# Voyage project iff it HOLDS brief.md or brief.md.draft under one of them.
|
||||
#
|
||||
# It walks the FILESYSTEM and never `git ls-files`. Measured: llm-ingestion-okf
|
||||
# gitignores .claude/projects/ (commit 087be0b, "keep .claude/projects/
|
||||
# local-only"), so an index-based detector would report ZERO briefs in the one
|
||||
# repo actually running a Voyage - a broken query returning a positive-looking
|
||||
# null. Selftest section 29 pins it with a fixture whose ignore status is
|
||||
# asserted as ground truth first.
|
||||
voyage_dirs() {
|
||||
for vd_g in "$1"/.claude/projects/*/ "$1"/docs/*/ "$1"/features/*/; do
|
||||
[ -d "$vd_g" ] || continue
|
||||
vd_p="${vd_g%/}"
|
||||
if [ -f "$vd_p/brief.md" ] || [ -f "$vd_p/brief.md.draft" ]; then
|
||||
printf '%s\n' "$vd_p"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# The phase ladder, furthest artifact present wins. This is the one field that
|
||||
# cannot be derived from a STATE.md at all, and it is the whole reason the row
|
||||
# exists: brief.md.draft (the review gate has not cleared) -> brief.md ->
|
||||
# research/*.md -> plan.md -> execute -> review.md.
|
||||
#
|
||||
# IT MEASURES ARTIFACTS, NOT SESSIONS, and the bound is stated rather than
|
||||
# hidden: /trekexecute leaves a file behind only in its MULTI-session form
|
||||
# (.session-state.local.json), so a plan executed in one session leaves nothing
|
||||
# and `plan` is the last thing the filesystem can prove. Nothing here says a
|
||||
# session is alive - the same refusal FLY carries, for the same reason.
|
||||
voyage_phase() {
|
||||
[ -f "$1/review.md" ] && { printf 'review'; return 0; }
|
||||
[ -f "$1/.session-state.local.json" ] && { printf 'execute'; return 0; }
|
||||
[ -f "$1/plan.md" ] && { printf 'plan'; return 0; }
|
||||
for vp_f in "$1"/research/*.md; do
|
||||
[ -f "$vp_f" ] && { printf 'research'; return 0; }
|
||||
done
|
||||
[ -f "$1/brief.md" ] && { printf 'brief'; return 0; }
|
||||
[ -f "$1/brief.md.draft" ] && { printf 'brief-draft'; return 0; }
|
||||
printf '?'
|
||||
}
|
||||
|
||||
# Days since the NEWEST artifact in the project - "nothing has happened here
|
||||
# for N days". This INVERTS the oldest-wins rule ORDRE and FLY carry, and the
|
||||
# inversion is deliberate rather than an oversight: an order queue's problem is
|
||||
# the oldest item still waiting, while a project's problem is that its most
|
||||
# recent activity is old. Both answer "how long has this been a problem".
|
||||
# `?` when a mtime could not be read, reported for the WHOLE reading: an
|
||||
# unmeasured file could be the newest one, which makes "newest" a claim this
|
||||
# function is no longer entitled to make.
|
||||
voyage_age() {
|
||||
# One `find` and one BATCHED `stat` (-exec ... +), never a stat per file: the
|
||||
# per-file form forked once per artifact and cost 10s over the real tree,
|
||||
# which would have made the plain table 4x slower for a column beside it.
|
||||
va_files="$(find "$1" -type f 2>/dev/null)"
|
||||
va_n="$(printf '%s\n' "$va_files" | grep -c . | tr -d ' ')"
|
||||
[ "${va_n:-0}" -gt 0 ] || { printf '?'; return 0; }
|
||||
va_ms="$(find "$1" -type f -exec stat -f %m {} + 2>/dev/null | grep '^[0-9][0-9]*$')"
|
||||
va_mn="$(printf '%s\n' "$va_ms" | grep -c . | tr -d ' ')"
|
||||
# A file counted but not dated means the reading is short by an unknown
|
||||
# amount, and the missing one could be the newest - so "newest" stops being a
|
||||
# claim this function is entitled to make. Reported for the WHOLE reading.
|
||||
[ "${va_mn:-0}" -eq "${va_n:-0}" ] || { printf '?'; return 0; }
|
||||
va_new="$(printf '%s\n' "$va_ms" | sort -n | tail -1)"
|
||||
case "$va_new" in ''|*[!0-9]*) printf '?'; return 0 ;; esac
|
||||
printf '%dd' $(( (NOW - va_new) / 86400 ))
|
||||
}
|
||||
|
||||
# brief_quality out of the FRONTMATTER BLOCK ONLY, never the body - a body line
|
||||
# is untrusted text and must not be able to speak for the brief, the same rule
|
||||
# coord-count.sh applies to `reply-expected`. Measured across the real tree
|
||||
# 2026-08-31: only 8 of ~40 briefs carry the field at all, so ABSENT must read
|
||||
# `-` and never `complete`. `partial` vs `complete` is precisely what three
|
||||
# presence-greps could not tell apart (the order's field 2).
|
||||
voyage_quality() {
|
||||
vq_f="$1/brief.md"
|
||||
[ -f "$vq_f" ] || vq_f="$1/brief.md.draft"
|
||||
[ -f "$vq_f" ] || { printf -- '-'; return 0; }
|
||||
vq_v="$(awk '
|
||||
NR==1 { if ($0 ~ /^---[[:space:]]*$/) { inf=1; next } else { exit } }
|
||||
inf && /^---[[:space:]]*$/ { exit }
|
||||
inf && /^brief_quality:[[:space:]]*/ {
|
||||
sub(/^brief_quality:[[:space:]]*/, "")
|
||||
gsub(/^["'"'"']|["'"'"']$/, "")
|
||||
gsub(/[[:space:]]+$/, "")
|
||||
print; exit
|
||||
}' "$vq_f" 2>/dev/null | head -1 | tr -d '\r')"
|
||||
[ -n "$vq_v" ] || vq_v="-"
|
||||
printf '%s' "$vq_v"
|
||||
}
|
||||
|
||||
# Open blocking decisions: `[BLOCKING DECISION, before S<n>]` in the brief.
|
||||
# Counted at DECLARATION SITES ONLY - the bracket must open the line's content
|
||||
# after list markers, headings and bold. Measured on the real brief: the string
|
||||
# occurs on four lines, of which ONE declares and three refer to it in prose,
|
||||
# so a bare `grep -c` reports 4 where the honest answer is 1. Selftest section
|
||||
# 29 carries the known-negative control (a brief whose only occurrences are
|
||||
# prose must count 0).
|
||||
#
|
||||
# It reports what the brief DECLARES and infers nothing about resolution: board
|
||||
# reports and refuses the inference, exactly as FLY does for liveness.
|
||||
# Prints "<count> <gates>" as two space-separated fields.
|
||||
voyage_blocking() {
|
||||
vb_f="$1/brief.md"
|
||||
[ -f "$vb_f" ] || vb_f="$1/brief.md.draft"
|
||||
[ -f "$vb_f" ] || { printf '? -'; return 0; }
|
||||
vb_re='^[[:space:]]*(#+[[:space:]]*)?([-*+][[:space:]]*)?(\*\*)?\[BLOCKING DECISION'
|
||||
vb_n="$(grep -cE "$vb_re" "$vb_f" 2>/dev/null | tr -d ' ')"
|
||||
case "$vb_n" in ''|*[!0-9]*) vb_n=0 ;; esac
|
||||
vb_g="$(grep -E "$vb_re" "$vb_f" 2>/dev/null \
|
||||
| sed -n 's/.*\[BLOCKING DECISION,[[:space:]]*before[[:space:]]*\([A-Za-z0-9._-]*\)\].*/\1/p' \
|
||||
| sort -u | paste -sd, - | tr -d ' ')"
|
||||
[ -n "$vb_g" ] || vb_g="-"
|
||||
printf '%s %s' "$vb_n" "$vb_g"
|
||||
}
|
||||
|
||||
# The research denominator (the order's field 5). A research step with NULL
|
||||
# output is ansikt 4 at programme level: research/README.md in the completed
|
||||
# run opened with "this directory held 0 files when the session started", and
|
||||
# nothing on any board could show that. Three outcomes, deliberately distinct:
|
||||
# "-" no research directory at all - the step was never started.
|
||||
# "0" the directory exists and holds nothing - started, produced nothing.
|
||||
# "N" measured.
|
||||
voyage_research() {
|
||||
[ -d "$1/research" ] || { printf -- '-'; return 0; }
|
||||
vr_n=0
|
||||
for vr_f in "$1"/research/*.md; do
|
||||
[ -f "$vr_f" ] && vr_n=$((vr_n+1))
|
||||
done
|
||||
printf '%d' "$vr_n"
|
||||
}
|
||||
|
||||
# The repo-level table cell: count, and the age of the STALEST project - the
|
||||
# one whose most recent activity is oldest, which is the analogue of ORDRE's
|
||||
# oldest-first rule. A bare `0` means no projects; "0d" would mean a project
|
||||
# touched today, and those are two different facts (the same reason FLY exists
|
||||
# beside ORDRE, and the same reason an empty order queue prints a bare 0).
|
||||
voyage_cell() {
|
||||
vc_n=0; vc_max=""; vc_unmeas=0
|
||||
while IFS= read -r vc_d; do
|
||||
[ -n "$vc_d" ] || continue
|
||||
vc_n=$((vc_n+1))
|
||||
vc_a="$(voyage_age "$vc_d")"
|
||||
case "$vc_a" in
|
||||
'?') vc_unmeas=1 ;;
|
||||
*) vc_a="${vc_a%d}"
|
||||
if [ -z "$vc_max" ] || [ "$vc_a" -gt "$vc_max" ]; then vc_max="$vc_a"; fi ;;
|
||||
esac
|
||||
done <<VOYAGE_CELL_EOF
|
||||
$(voyage_dirs "$1")
|
||||
VOYAGE_CELL_EOF
|
||||
[ "$vc_n" -eq 0 ] && { printf '0'; return 0; }
|
||||
if [ "$vc_unmeas" -eq 1 ] || [ -z "$vc_max" ]; then printf '%d:?' "$vc_n"; return 0; fi
|
||||
printf '%d:%dd' "$vc_n" "$vc_max"
|
||||
}
|
||||
|
||||
printf '%s\n' "$REPOS" | while IFS= read -r d; do
|
||||
[ -n "$d" ] || continue
|
||||
name="$(basename "$d")"
|
||||
|
|
@ -598,9 +763,13 @@ printf '%s\n' "$REPOS" | while IFS= read -r d; do
|
|||
# `orders` sits between blockedon and neste, never after it: `neste` is free
|
||||
# prose lifted out of a STATE.md and may contain a literal '|', so it has to
|
||||
# stay the last field on the line.
|
||||
printf '%s|%06d|%s|%s|%s|%s|%s|%sd|%s|%s|%s|%s|%s|%s|%s|%s\n' \
|
||||
# `voy` sits between claimedage and neste, never after it: `neste` is free
|
||||
# prose lifted out of a STATE.md and may contain a literal '|', so it has to
|
||||
# stay the last field on the line.
|
||||
voy="$(voyage_cell "$d")"
|
||||
printf '%s|%06d|%s|%s|%s|%s|%s|%sd|%s|%s|%s|%s|%s|%s|%s|%s|%s\n' \
|
||||
"$bucket" "$age" "$name" "$disp" "$cost" "$inbox" "$dirty" "$age" "$lastcol" "$d" \
|
||||
"${blockedon:--}" "$orders" "$ordersage" "$claimed" "$claimedage" "$neste"
|
||||
"${blockedon:--}" "$orders" "$ordersage" "$claimed" "$claimedage" "$voy" "$neste"
|
||||
done > "${TMPDIR:-/tmp}/board.$$"
|
||||
|
||||
RECORDS="${TMPDIR:-/tmp}/board.$$"
|
||||
|
|
@ -757,20 +926,20 @@ awk -F'|' '$4 ~ /^blocked/ && $11 != "-" && $11 != "" {print $11}' "$RECORDS" \
|
|||
|
||||
hdr() {
|
||||
printf '\n%s\n' "$1"
|
||||
printf '%-32s %-34s %-14s %4s %7s %7s %4s %6s %6s %s\n' \
|
||||
"REPO" "STATUS" "KOST" "INN" "ORDRE" "FLY" "DRT" "ALDER" "SISTE" "NESTE"
|
||||
printf '%-32s %-34s %-14s %4s %7s %7s %7s %4s %6s %6s %s\n' \
|
||||
"REPO" "STATUS" "KOST" "INN" "ORDRE" "FLY" "VOY" "DRT" "ALDER" "SISTE" "NESTE"
|
||||
}
|
||||
|
||||
rows() {
|
||||
awk -F'|' -v b="$1" '$1==b' "$RECORDS" | sort -t'|' -k2,2n | \
|
||||
while IFS='|' read -r bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage neste; do
|
||||
while IFS='|' read -r bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage voy neste; do
|
||||
# `N:age` only when there is something to date. An empty queue prints a
|
||||
# bare 0, never "0:0d" - absence must not borrow the shape of a
|
||||
# measurement.
|
||||
o_cell="$orders"; [ "$ordersage" = "-" ] || o_cell="$orders:$ordersage"
|
||||
c_cell="$claimed"; [ "$claimedage" = "-" ] || c_cell="$claimed:$claimedage"
|
||||
printf '%-32s %-34s %-14s %4s %7s %7s %4s %6s %6s %s\n' \
|
||||
"$name" "$status" "$cost" "$inbox" "$o_cell" "$c_cell" "$dirty" "$age" "$last" "$(trunc "$neste" "$NESTE_WIDTH")"
|
||||
printf '%-32s %-34s %-14s %4s %7s %7s %7s %4s %6s %6s %s\n' \
|
||||
"$name" "$status" "$cost" "$inbox" "$o_cell" "$c_cell" "$voy" "$dirty" "$age" "$last" "$(trunc "$neste" "$NESTE_WIDTH")"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -960,7 +1129,7 @@ brief() {
|
|||
FILENAME==OWF { ow[$1] = $2 + 0; next }
|
||||
{ name = $3; owed = (name in ow) ? ow[name] : 0; if (owed > 0) print owed, $0 }
|
||||
' "$OWED" "$RECORDS" | sort -t'|' -k1,1nr | \
|
||||
while IFS='|' read -r owed bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage neste; do
|
||||
while IFS='|' read -r owed bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage voy neste; do
|
||||
printf ' %-32s INN %-4s %s\n' "$name" "$owed" "$status"
|
||||
# Wrapped, not cut - the whole line is the point, but a 500-character one
|
||||
# is unreadable in a file nobody watched being written. Locale is set for
|
||||
|
|
@ -1564,7 +1733,7 @@ plan() {
|
|||
echo ""
|
||||
|
||||
pn=0
|
||||
while IFS='|' read -r why bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage neste; do
|
||||
while IFS='|' read -r why bucket sortkey name status cost inbox dirty age last dir blockedon orders ordersage claimed claimedage voy neste; do
|
||||
[ -n "$name" ] || continue
|
||||
pn=$((pn + 1))
|
||||
printf 'tab=%s\n' "$pn"
|
||||
|
|
@ -1624,7 +1793,7 @@ inbox_plan() {
|
|||
FILENAME==RCF {
|
||||
name = $3
|
||||
rc_bucket[name] = $1; rc_status[name] = $4; rc_cost[name] = $5
|
||||
rc_dir[name] = $10; rc_neste[name] = $16
|
||||
rc_dir[name] = $10; rc_neste[name] = $17
|
||||
next
|
||||
}
|
||||
{
|
||||
|
|
@ -1691,6 +1860,105 @@ if [ "$INBOX_PLAN" -eq 1 ]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# --- Voyage rendering (--voyage) -------------------------------------------
|
||||
# A SIXTH rendering of the same scan, never a second scan, and read-only like
|
||||
# every other one: it writes to no repo, no STATE.md and no mailbox.
|
||||
#
|
||||
# It answers the question no single STATE.md can - which briefs are in flight,
|
||||
# in what phase, and who is waiting on whom - as key=value blocks, the format
|
||||
# this file already uses for its machine-readable renderings. Prose would make
|
||||
# the format an API no test in THIS repo could hold stable for a consumer in
|
||||
# another.
|
||||
#
|
||||
# "Who waits on whom" has exactly two answerable forms here, and neither is
|
||||
# guessed: an OPEN BLOCKING DECISION means the project waits on the operator
|
||||
# (measured on the completed run - one decision gated /trekplan for a full day
|
||||
# and nothing on any board showed it), and the repo's PENDING ORDER IDS are
|
||||
# carried beside the phase, because that coupling is what lets a reader go from
|
||||
# "this brief is standing still" to "this order is pending".
|
||||
voyage() {
|
||||
vy_repos=0; vy_with=0; vy_total=0
|
||||
|
||||
echo "VOYAGE - briefer i omloep"
|
||||
# The legend is engine behaviour, exactly as FLY's liveness denial is: a
|
||||
# column or a field whose form nothing pins is one that drifts.
|
||||
echo "# FASE maaler ARTEFAKTER, ikke sesjoner. En plan utfoert i EN sesjon legger"
|
||||
echo "# ingen fil igjen, saa 'plan' er det siste filsystemet kan bevise. Ingenting"
|
||||
echo "# her sier at en sesjon lever - samme nekt som FLY baerer."
|
||||
echo "# KVALITET er brief_quality fra frontmatter. '-' betyr at feltet MANGLER,"
|
||||
echo "# aldri at briefen er complete. RESEARCH: '-' = ingen katalog, '0' = katalog"
|
||||
echo "# uten output (et research-steg med null resultat, ansikt 4 paa programnivaa)."
|
||||
echo "# ALDER = dager siden NYESTE artefakt: hvor lenge ingenting har skjedd. Det er"
|
||||
echo "# motsatt av ORDRE/FLY med vilje - en ko sitt problem er det eldste som venter,"
|
||||
echo "# et prosjekt sitt problem er at siste aktivitet er gammel."
|
||||
echo ""
|
||||
|
||||
while IFS='|' read -r vy_name vy_dir; do
|
||||
[ -n "$vy_name" ] || continue
|
||||
vy_repos=$((vy_repos+1))
|
||||
vy_here=0
|
||||
|
||||
# Order coupling: the repo's PENDING orders (claimed ones are in flight and
|
||||
# are not what a reader is looking for when a brief has stopped moving).
|
||||
vy_ord=0; vy_ids=""
|
||||
for vy_of in "$COORD/$vy_name/orders"/*.md; do
|
||||
[ -f "$vy_of" ] || continue
|
||||
vy_ord=$((vy_ord+1))
|
||||
vy_ids="$vy_ids$(basename "$vy_of" .md)
|
||||
"
|
||||
done
|
||||
|
||||
while IFS= read -r vy_p; do
|
||||
[ -n "$vy_p" ] || continue
|
||||
vy_here=1; vy_total=$((vy_total+1))
|
||||
vy_rel="${vy_p#$vy_dir/}"
|
||||
vy_bl="$(voyage_blocking "$vy_p")"
|
||||
vy_bn="${vy_bl%% *}"; vy_bg="${vy_bl##* }"
|
||||
case "$vy_bn" in
|
||||
0|'?') vy_wait="-" ;;
|
||||
*) vy_wait="operatoerbeslutning" ;;
|
||||
esac
|
||||
printf 'prosjekt=%s/%s\n' "$vy_name" "$vy_rel"
|
||||
printf 'repo=%s\n' "$vy_name"
|
||||
printf 'dir=%s\n' "$vy_p"
|
||||
printf 'fase=%s\n' "$(voyage_phase "$vy_p")"
|
||||
printf 'kvalitet=%s\n' "$(voyage_quality "$vy_p")"
|
||||
printf 'blokkerende=%s\n' "$vy_bn"
|
||||
printf 'blokkerende_gate=%s\n' "$vy_bg"
|
||||
printf 'venter=%s\n' "$vy_wait"
|
||||
printf 'research=%s\n' "$(voyage_research "$vy_p")"
|
||||
printf 'alder=%s\n' "$(voyage_age "$vy_p")"
|
||||
printf 'ordre=%s\n' "$vy_ord"
|
||||
printf '%s' "$vy_ids" | while IFS= read -r vy_i; do
|
||||
[ -n "$vy_i" ] && printf 'ordre_id=%s\n' "$vy_i"
|
||||
done
|
||||
echo ""
|
||||
done <<VOYAGE_PROJ_EOF
|
||||
$(voyage_dirs "$vy_dir")
|
||||
VOYAGE_PROJ_EOF
|
||||
|
||||
[ "$vy_here" -eq 1 ] && vy_with=$((vy_with+1))
|
||||
done <<VOYAGE_REPO_EOF
|
||||
$(awk -F'|' '{print $3 "|" $10}' "$RECORDS")
|
||||
VOYAGE_REPO_EOF
|
||||
|
||||
# The denominator, always - "nothing found" and "not measured" must never
|
||||
# render as the same empty page. This is the whole ansikt-4 rule applied to
|
||||
# this renderer's own output.
|
||||
if [ "$vy_total" -eq 0 ]; then
|
||||
printf 'ingen=0 Voyage-prosjekter funnet i %s skannede repo\n' "$vy_repos"
|
||||
printf '# Det er et MAALT null, ikke en tom side: %s repo ble undersoekt paa de\n' "$vy_repos"
|
||||
echo "# tre anerkjente plasseringene (.claude/projects/, docs/, features/)."
|
||||
fi
|
||||
printf '# nevner: %s repo skannet, %s med minst ett Voyage-prosjekt, %s prosjekter\n' \
|
||||
"$vy_repos" "$vy_with" "$vy_total"
|
||||
}
|
||||
|
||||
if [ "$VOYAGE" -eq 1 ]; then
|
||||
voyage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
count() { awk -F'|' -v b="$1" '$1==b' "$RECORDS" | wc -l | tr -d ' '; }
|
||||
|
||||
echo "BOARD - tverr-repo oppmerksomhetstavle ($(awk 'END{print NR}' "$RECORDS") repo)"
|
||||
|
|
@ -1712,6 +1980,13 @@ echo "FLY = ordrer i arbeid (claimed). Beviser aldri at en okt lever eller er ak
|
|||
echo " Beviser bare at noen TOK ordren; ingenting frigir den om okten dor."
|
||||
echo " Summeres ALDRI med ORDRE - samme ko, annen tilstand."
|
||||
echo " Baerer samme N:Md - alderen paa den ELDSTE claimen. Ingenting frigir den."
|
||||
# VOY is the same class of reading as ORDRE and FLY - a durable filesystem fact
|
||||
# about work sitting in this repo - and like them it is never summed with them.
|
||||
# The per-project detail (phase, brief_quality, blocking decisions) lives in
|
||||
# --voyage: a single cell cannot carry a phase when a repo holds seven briefs.
|
||||
echo "VOY = Voyage-prosjekter (brief i omloep). N:Md = antall og alderen paa det"
|
||||
echo " STALESTE prosjektet - dager siden NYESTE artefakt der, dvs. hvor lenge"
|
||||
echo " ingenting har skjedd. Bart 0 = ingen prosjekter. Detaljer: --voyage."
|
||||
echo "ALDER = dager siden STATE.md endret. SISTE = dager siden siste commit."
|
||||
|
||||
[ "$(count 1)" -gt 0 ] && { hdr "BLOKKERT (venter paa ekstern avhengighet)"; rows 1; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue