@indigoai-us/hq-cli 5.345.88 → 5.345.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/assets/scaffold/core/scripts/hq-delegate-bundle.sh +107 -21
- package/assets/scaffold/core/scripts/hq-delegate-publish.sh +36 -5
- package/assets/scaffold/core/scripts/hq-delegate-send.sh +18 -3
- package/assets/scaffold/core/scripts/hq-delegate-transfer.sh +74 -26
- package/assets/scaffold/core/scripts/hq-delegate-verify.sh +10 -1
- package/assets/scaffold/core/scripts/lib/delegate-brainstorm.sh +120 -0
- package/dist/commands/core-delegate-brainstorm.d.ts +9 -0
- package/dist/commands/core-delegate-brainstorm.js +161 -0
- package/dist/commands/core-delegate-w1b.js +134 -40
- package/dist/commands/core-delegate.d.ts +2 -0
- package/dist/commands/core-delegate.js +56 -29
- package/dist/commands/core.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.89] — 2026-10-11
|
|
6
|
+
- Delegation commands now support projects that have a brainstorm and dossier but no PRD yet. The native route and bundled shell fallback build, transfer, publish, and send the brainstorm-stage handoff consistently.
|
|
7
|
+
|
|
5
8
|
## [5.345.88] — 2026-10-11
|
|
6
9
|
- Work Mesh no longer crash-loops through memory during an internet outage:
|
|
7
10
|
failed presence and membership lookups back off and log once per drop, and a
|
|
@@ -16,9 +16,16 @@
|
|
|
16
16
|
# prompt) — schema documented in
|
|
17
17
|
# core/knowledge/public/hq-core/delegation-bundle-spec.md.
|
|
18
18
|
#
|
|
19
|
+
# Source of truth is prd.json. When the project has no prd.json yet but has a
|
|
20
|
+
# brainstorm.md (a brainstorm-stage project, policy
|
|
21
|
+
# hq-brainstorm-leaves-handoffable-project-folder), brainstorm.md is the source
|
|
22
|
+
# of truth: the dossier is brainstorm.md, research/**, and the newest journal
|
|
23
|
+
# note; the brief says so and recommends /plan as the next step.
|
|
24
|
+
#
|
|
19
25
|
# Fails closed (non-zero, nothing written) when:
|
|
20
26
|
# - required arguments are missing
|
|
21
|
-
# - the project directory
|
|
27
|
+
# - the project directory does not exist, or it has neither prd.json nor
|
|
28
|
+
# brainstorm.md, or its prd.json parses invalid
|
|
22
29
|
# - the generated manifest or BRIEF matches a secret-detection pattern
|
|
23
30
|
#
|
|
24
31
|
# Env:
|
|
@@ -31,6 +38,8 @@ HQ_ROOT="${HQ_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
|
31
38
|
|
|
32
39
|
# shellcheck source=lib/secret-patterns.sh
|
|
33
40
|
. "$SCRIPT_DIR/lib/secret-patterns.sh"
|
|
41
|
+
# shellcheck source=lib/delegate-brainstorm.sh
|
|
42
|
+
. "$SCRIPT_DIR/lib/delegate-brainstorm.sh"
|
|
34
43
|
|
|
35
44
|
usage() {
|
|
36
45
|
sed -n '3,13p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
@@ -70,10 +79,16 @@ case "$MODE" in transfer|share) ;; *) die "--mode must be transfer or share" ;;
|
|
|
70
79
|
case "$TO_KIND" in person|agent) ;; *) die "--to-kind must be person or agent" ;; esac
|
|
71
80
|
|
|
72
81
|
PROJECT_DIR="$HQ_ROOT/companies/$COMPANY/projects/$PROJECT"
|
|
82
|
+
PROJECT_REL="companies/$COMPANY/projects/$PROJECT"
|
|
73
83
|
PRD="$PROJECT_DIR/prd.json"
|
|
74
|
-
|
|
75
|
-
[ -
|
|
76
|
-
|
|
84
|
+
BRAINSTORM="$PROJECT_DIR/brainstorm.md"
|
|
85
|
+
[ -d "$PROJECT_DIR" ] || die "project directory not found: $PROJECT_REL"
|
|
86
|
+
STAGE="$(hq_delegate_project_stage "$PROJECT_DIR")"
|
|
87
|
+
case "$STAGE" in
|
|
88
|
+
prd) jq -e . "$PRD" >/dev/null 2>&1 || die "prd.json is not valid JSON: $PRD" ;;
|
|
89
|
+
brainstorm) ;;
|
|
90
|
+
*) die "project has neither prd.json nor brainstorm.md: $PROJECT_REL (run /brainstorm or /plan first)" ;;
|
|
91
|
+
esac
|
|
77
92
|
|
|
78
93
|
# --- identity ----------------------------------------------------------------
|
|
79
94
|
|
|
@@ -104,8 +119,14 @@ PROJECT_PREFIX="projects/$PROJECT/"
|
|
|
104
119
|
|
|
105
120
|
# knowledge entries from prd metadata: split into vault-grantable (under this
|
|
106
121
|
# company), policies (under this company's policies/), and repo-based docs
|
|
107
|
-
# (recorded for the brief, never granted as vault prefixes).
|
|
108
|
-
|
|
122
|
+
# (recorded for the brief, never granted as vault prefixes). A brainstorm-stage
|
|
123
|
+
# project keeps its research inside the project folder, so there is nothing
|
|
124
|
+
# outside the dossier prefix to grant.
|
|
125
|
+
if [ "$STAGE" = prd ]; then
|
|
126
|
+
KNOWLEDGE_JSON="$(jq -c '[.metadata.knowledge // [] | .[]]' "$PRD")"
|
|
127
|
+
else
|
|
128
|
+
KNOWLEDGE_JSON="[]"
|
|
129
|
+
fi
|
|
109
130
|
|
|
110
131
|
VAULT_READ_PREFIXES="$(printf '%s' "$KNOWLEDGE_JSON" | jq -r --arg co "$COMPANY" '
|
|
111
132
|
[ .[]
|
|
@@ -142,7 +163,8 @@ printf '%s' "$VAULT_PREFIXES_JSON" | jq -e '
|
|
|
142
163
|
|
|
143
164
|
# --- repo block (recorded here; verified/pushed by the US-004 step) ----------
|
|
144
165
|
|
|
145
|
-
REPO_PATH="
|
|
166
|
+
REPO_PATH=""
|
|
167
|
+
[ "$STAGE" = prd ] && REPO_PATH="$(jq -r '.metadata.repoPath // empty' "$PRD")"
|
|
146
168
|
if [ -n "$REPO_PATH" ]; then
|
|
147
169
|
REPO_JSON="$(jq -cn \
|
|
148
170
|
--arg path "$REPO_PATH" \
|
|
@@ -156,14 +178,33 @@ fi
|
|
|
156
178
|
|
|
157
179
|
# --- board id ----------------------------------------------------------------
|
|
158
180
|
|
|
181
|
+
# A PRD project is matched by prd_path; a brainstorm-stage entry (status
|
|
182
|
+
# exploring) carries brainstorm_path and a null prd_path.
|
|
159
183
|
BOARD_ID="null"
|
|
160
184
|
BOARD_FILE="$HQ_ROOT/companies/$COMPANY/board.json"
|
|
161
185
|
if [ -f "$BOARD_FILE" ]; then
|
|
162
|
-
BOARD_ID="$(jq --arg prd "
|
|
163
|
-
'[.projects // [] | .[] | select(.prd_path == $prd) | .id] | first // null' \
|
|
186
|
+
BOARD_ID="$(jq --arg prd "$PROJECT_REL/prd.json" --arg bs "$PROJECT_REL/brainstorm.md" \
|
|
187
|
+
'[.projects // [] | .[] | select(.prd_path == $prd or .brainstorm_path == $bs) | .id] | first // null' \
|
|
164
188
|
"$BOARD_FILE" 2>/dev/null || echo null)"
|
|
165
189
|
fi
|
|
166
190
|
|
|
191
|
+
# --- stage-specific project block ---------------------------------------------
|
|
192
|
+
#
|
|
193
|
+
# prdPath stays the PRD contract for every downstream helper; a brainstorm-stage
|
|
194
|
+
# project sets it to null, names brainstorm.md as sourcePath, and lists the
|
|
195
|
+
# dossier files (brainstorm.md, research/**, newest journal note) that the
|
|
196
|
+
# publish step must ship.
|
|
197
|
+
|
|
198
|
+
if [ "$STAGE" = prd ]; then
|
|
199
|
+
PROJECT_JSON="$(jq -cn --arg name "$PROJECT" --arg prd "$PROJECT_REL/prd.json" --argjson boardId "$BOARD_ID" \
|
|
200
|
+
'{name: $name, stage: "prd", prdPath: $prd, sourcePath: $prd, boardId: $boardId}')"
|
|
201
|
+
else
|
|
202
|
+
DOSSIER_JSON="$(hq_brainstorm_dossier "$HQ_ROOT" "$PROJECT_REL" | jq -R . | jq -cs .)"
|
|
203
|
+
PROJECT_JSON="$(jq -cn --arg name "$PROJECT" --arg bs "$PROJECT_REL/brainstorm.md" \
|
|
204
|
+
--argjson boardId "$BOARD_ID" --argjson dossier "$DOSSIER_JSON" \
|
|
205
|
+
'{name: $name, stage: "brainstorm", prdPath: null, sourcePath: $bs, boardId: $boardId, dossier: $dossier}')"
|
|
206
|
+
fi
|
|
207
|
+
|
|
167
208
|
# --- checksums of the project dossier ----------------------------------------
|
|
168
209
|
|
|
169
210
|
CHECKSUMS_JSON="$(
|
|
@@ -176,8 +217,8 @@ CHECKSUMS_JSON="$(
|
|
|
176
217
|
|
|
177
218
|
# --- build in a temp dir, scan, then move into place -------------------------
|
|
178
219
|
|
|
179
|
-
|
|
180
|
-
trap 'rm -rf "$
|
|
220
|
+
STAGE_DIR="$(mktemp -d)"
|
|
221
|
+
trap 'rm -rf "$STAGE_DIR"' EXIT
|
|
181
222
|
|
|
182
223
|
jq -n \
|
|
183
224
|
--arg id "$DELEGATION_ID" \
|
|
@@ -189,9 +230,7 @@ jq -n \
|
|
|
189
230
|
--arg toPrincipal "$TO" \
|
|
190
231
|
--arg toName "$TO_NAME" \
|
|
191
232
|
--arg company "$COMPANY" \
|
|
192
|
-
--
|
|
193
|
-
--arg prdPath "companies/$COMPANY/projects/$PROJECT/prd.json" \
|
|
194
|
-
--argjson boardId "$BOARD_ID" \
|
|
233
|
+
--argjson project "$PROJECT_JSON" \
|
|
195
234
|
--argjson vaultPrefixes "$VAULT_PREFIXES_JSON" \
|
|
196
235
|
--argjson repo "$REPO_JSON" \
|
|
197
236
|
--argjson knowledge "$MANIFEST_KNOWLEDGE" \
|
|
@@ -207,7 +246,7 @@ jq -n \
|
|
|
207
246
|
to: {kind: $toKind, principal: $toPrincipal,
|
|
208
247
|
displayName: (if $toName == "" then null else $toName end)},
|
|
209
248
|
company: $company,
|
|
210
|
-
project:
|
|
249
|
+
project: $project,
|
|
211
250
|
vaultPrefixes: $vaultPrefixes,
|
|
212
251
|
repo: $repo,
|
|
213
252
|
secrets: [],
|
|
@@ -215,10 +254,11 @@ jq -n \
|
|
|
215
254
|
policies: $policies,
|
|
216
255
|
checksums: $checksums,
|
|
217
256
|
status: "building"
|
|
218
|
-
}' > "$
|
|
257
|
+
}' > "$STAGE_DIR/manifest.json"
|
|
219
258
|
|
|
220
259
|
# --- BRIEF.md — full prose for a reader who has never seen the project -------
|
|
221
260
|
|
|
261
|
+
if [ "$STAGE" = prd ]; then
|
|
222
262
|
DESCRIPTION="$(jq -r '.description // "No description recorded."' "$PRD")"
|
|
223
263
|
GOAL="$(jq -r '.metadata.goal // empty' "$PRD")"
|
|
224
264
|
TOTAL_STORIES="$(jq -r '[.userStories // [] | .[]] | length' "$PRD")"
|
|
@@ -278,19 +318,65 @@ TRAPS="$(jq -r '[.metadata.securityNotes // empty, (.metadata.executionConventio
|
|
|
278
318
|
echo "## Where everything lives"
|
|
279
319
|
echo
|
|
280
320
|
echo "The full PRD (every story, acceptance criteria, and decision history) is at \`companies/$COMPANY/projects/$PROJECT/prd.json\`. The pickup prompt that accompanied this brief pulls every file you need on demand — you do not need to run a full sync."
|
|
281
|
-
} > "$
|
|
321
|
+
} > "$STAGE_DIR/BRIEF.md"
|
|
322
|
+
else
|
|
323
|
+
# Brainstorm stage: no stories, no PRD. The brief says so plainly and points
|
|
324
|
+
# the recipient at /plan.
|
|
325
|
+
SUMMARY="$(hq_brainstorm_summary "$BRAINSTORM")"
|
|
326
|
+
[ -n "$SUMMARY" ] || SUMMARY="No summary recorded in brainstorm.md."
|
|
327
|
+
RECOMMENDATION="$(hq_brainstorm_section "$BRAINSTORM" "Recommendation")"
|
|
328
|
+
UNKNOWNS="$(hq_brainstorm_section "$BRAINSTORM" "What We Don't Know")"
|
|
329
|
+
DOSSIER_LIST="$(printf '%s' "$PROJECT_JSON" | jq -r '.dossier[] | "- `" + . + "`"')"
|
|
330
|
+
{
|
|
331
|
+
echo "# Delegation brief — $PROJECT"
|
|
332
|
+
echo
|
|
333
|
+
echo "This brief describes the **$PROJECT** project in the **$COMPANY** company. Completed handoff steps are recorded in the manifest. Pull the files and acknowledge receipt; report any access error to the sender."
|
|
334
|
+
echo
|
|
335
|
+
echo "## What this project is"
|
|
336
|
+
echo
|
|
337
|
+
echo "$SUMMARY"
|
|
338
|
+
echo
|
|
339
|
+
echo "## Where things stand"
|
|
340
|
+
echo
|
|
341
|
+
echo "This project is at the brainstorm stage. There is no PRD yet and no stories have been written; the source of truth is \`$PROJECT_REL/brainstorm.md\`, with research notes and the latest session journal alongside it."
|
|
342
|
+
echo
|
|
343
|
+
if [ -n "$RECOMMENDATION" ]; then
|
|
344
|
+
echo "## Recommendation so far"
|
|
345
|
+
echo
|
|
346
|
+
echo "$RECOMMENDATION"
|
|
347
|
+
echo
|
|
348
|
+
fi
|
|
349
|
+
if [ -n "$UNKNOWNS" ]; then
|
|
350
|
+
echo "## Open questions"
|
|
351
|
+
echo
|
|
352
|
+
echo "$UNKNOWNS"
|
|
353
|
+
echo
|
|
354
|
+
fi
|
|
355
|
+
echo "## Recommended next step"
|
|
356
|
+
echo
|
|
357
|
+
echo "Run \`/plan $PROJECT\` to turn the brainstorm into a PRD with stories. Read brainstorm.md and the research notes first; the recommendation and open questions above come from them."
|
|
358
|
+
echo
|
|
359
|
+
echo "## Where everything lives"
|
|
360
|
+
echo
|
|
361
|
+
echo "The dossier for this handoff:"
|
|
362
|
+
echo
|
|
363
|
+
echo "$DOSSIER_LIST"
|
|
364
|
+
echo
|
|
365
|
+
echo "The pickup prompt that accompanied this brief pulls every file you need on demand — you do not need to run a full sync."
|
|
366
|
+
} > "$STAGE_DIR/BRIEF.md"
|
|
367
|
+
fi
|
|
282
368
|
|
|
283
369
|
# --- fail-closed secret scan -------------------------------------------------
|
|
284
370
|
|
|
285
|
-
if ! hq_scan_secrets "$
|
|
371
|
+
if ! hq_scan_secrets "$STAGE_DIR/manifest.json" "$STAGE_DIR/BRIEF.md"; then
|
|
286
372
|
die "generated bundle matched a secret-detection pattern — nothing written (see stderr above)"
|
|
287
373
|
fi
|
|
288
374
|
|
|
289
375
|
# --- dry run: print the manifest, write nothing ------------------------------
|
|
290
376
|
|
|
291
377
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
292
|
-
cat "$
|
|
293
|
-
rm -rf "$
|
|
378
|
+
cat "$STAGE_DIR/manifest.json"
|
|
379
|
+
rm -rf "$STAGE_DIR"
|
|
294
380
|
trap - EXIT
|
|
295
381
|
echo "hq-delegate-bundle: dry run — nothing written" >&2
|
|
296
382
|
exit 0
|
|
@@ -300,7 +386,7 @@ fi
|
|
|
300
386
|
|
|
301
387
|
DEST="$HQ_ROOT/workspace/delegations/$DELEGATION_ID"
|
|
302
388
|
mkdir -p "$(dirname "$DEST")"
|
|
303
|
-
mv "$
|
|
389
|
+
mv "$STAGE_DIR" "$DEST"
|
|
304
390
|
trap - EXIT
|
|
305
391
|
|
|
306
392
|
echo "$DELEGATION_ID"
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# hq-core: public
|
|
3
3
|
# Publish final delegation artifacts and verify their canonical cloud bytes.
|
|
4
|
+
# PRD stage snapshots prd.json, README.md, and the delegation journal.
|
|
5
|
+
# Brainstorm stage (manifest project.stage == "brainstorm") snapshots the
|
|
6
|
+
# manifest's dossier list (brainstorm.md, research/**, newest journal note)
|
|
7
|
+
# plus the delegation journal, and checks ownership in the brainstorm.md
|
|
8
|
+
# frontmatter instead of prd.json.
|
|
4
9
|
set -euo pipefail
|
|
5
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
11
|
HQ_ROOT="${HQ_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
7
12
|
. "$SCRIPT_DIR/lib/secret-patterns.sh"
|
|
13
|
+
. "$SCRIPT_DIR/lib/delegate-brainstorm.sh"
|
|
8
14
|
die() { echo "hq-delegate-publish: $*" >&2; exit 1; }
|
|
9
15
|
[ "${1:-}" = --manifest ] && [ -f "${2:-}" ] || die 'usage: --manifest <path>'
|
|
10
16
|
MANIFEST="$2"
|
|
@@ -33,10 +39,19 @@ REL="companies/$COMPANY/projects/$PROJECT"
|
|
|
33
39
|
PROJECT_DIR="$HQ_ROOT/$REL"
|
|
34
40
|
BUNDLE="$(cd "$(dirname "$MANIFEST")" && pwd)"
|
|
35
41
|
[ -f "$BUNDLE/BRIEF.md" ] || die 'BRIEF.md missing'
|
|
36
|
-
|
|
42
|
+
STAGE_KIND="$(jq -r '.project.stage // "prd"' "$MANIFEST")"
|
|
43
|
+
case "$STAGE_KIND" in
|
|
44
|
+
prd) [ -f "$PROJECT_DIR/prd.json" ] || die 'PRD missing' ;;
|
|
45
|
+
brainstorm) [ -f "$PROJECT_DIR/brainstorm.md" ] || die 'brainstorm.md missing' ;;
|
|
46
|
+
*) die "unknown project stage '$STAGE_KIND'" ;;
|
|
47
|
+
esac
|
|
37
48
|
if [ "$(jq -r '.mode' "$MANIFEST")" = transfer ]; then
|
|
38
49
|
EXPECTED_OWNER="$(jq -r '.to.principal' "$MANIFEST")"
|
|
39
|
-
|
|
50
|
+
if [ "$STAGE_KIND" = prd ]; then
|
|
51
|
+
jq -e --arg owner "$EXPECTED_OWNER" '.metadata.owner == $owner' "$PROJECT_DIR/prd.json" >/dev/null || die 'local PRD owner does not match recipient'
|
|
52
|
+
else
|
|
53
|
+
[ "$(hq_brainstorm_frontmatter_get "$PROJECT_DIR/brainstorm.md" owner)" = "$EXPECTED_OWNER" ] || die 'local brainstorm.md owner does not match recipient'
|
|
54
|
+
fi
|
|
40
55
|
fi
|
|
41
56
|
STAGE="$(mktemp -d)"
|
|
42
57
|
atomic_copy() {
|
|
@@ -52,10 +67,26 @@ file_hash() {
|
|
|
52
67
|
}
|
|
53
68
|
# Snapshot final project content, excluding mutable/self-referential receipts.
|
|
54
69
|
: > "$STAGE/hashes"
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
if [ "$STAGE_KIND" = prd ]; then
|
|
71
|
+
SNAPSHOT_FILES="$PROJECT_DIR/prd.json
|
|
72
|
+
$PROJECT_DIR/README.md
|
|
73
|
+
$PROJECT_DIR/journal/delegations.md"
|
|
74
|
+
else
|
|
75
|
+
SNAPSHOT_FILES="$(jq -r '.project.dossier[]' "$MANIFEST" | sed "s|^|$HQ_ROOT/|")
|
|
76
|
+
$PROJECT_DIR/journal/delegations.md"
|
|
77
|
+
# Every dossier file the manifest promised must still exist.
|
|
78
|
+
while IFS= read -r file; do
|
|
79
|
+
[ -f "$file" ] || die "dossier file missing: ${file#"$HQ_ROOT/"}"
|
|
80
|
+
done <<EOF
|
|
81
|
+
$(jq -r '.project.dossier[]' "$MANIFEST" | sed "s|^|$HQ_ROOT/|")
|
|
82
|
+
EOF
|
|
83
|
+
fi
|
|
84
|
+
while IFS= read -r file; do
|
|
85
|
+
[ -n "$file" ] && [ -f "$file" ] || continue
|
|
57
86
|
jq -cn --arg key "${file#"$HQ_ROOT/"}" --arg value "$(file_hash "$file")" '{key:$key,value:$value}' >> "$STAGE/hashes"
|
|
58
|
-
done
|
|
87
|
+
done <<EOF
|
|
88
|
+
$SNAPSHOT_FILES
|
|
89
|
+
EOF
|
|
59
90
|
jq -cn --arg key "$REL/delegation/$ID/BRIEF.md" --arg value "$(file_hash "$STAGE/BRIEF.md")" '{key:$key,value:$value}' >> "$STAGE/hashes"
|
|
60
91
|
jq -s 'from_entries' "$STAGE/hashes" > "$STAGE/checksums"
|
|
61
92
|
jq --slurpfile hashes "$STAGE/checksums" '.checksums=$hashes[0] | .status="granted" | .recipientAccess="unconfirmed" | del(.publication,.verifiedAt,.sentAt,.dmEventId)' "$MANIFEST" > "$STAGE/manifest.json"
|
|
@@ -34,6 +34,8 @@ HQ_ROOT="${HQ_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
|
34
34
|
|
|
35
35
|
# shellcheck source=lib/secret-patterns.sh
|
|
36
36
|
. "$SCRIPT_DIR/lib/secret-patterns.sh"
|
|
37
|
+
# shellcheck source=lib/delegate-brainstorm.sh
|
|
38
|
+
. "$SCRIPT_DIR/lib/delegate-brainstorm.sh"
|
|
37
39
|
|
|
38
40
|
usage() { sed -n '3,27p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; }
|
|
39
41
|
die() { echo "hq-delegate-send: $*" >&2; exit 1; }
|
|
@@ -100,12 +102,25 @@ MODE="$(jq -r '.mode // "transfer"' "$MANIFEST")"
|
|
|
100
102
|
[ -n "$PROJECT" ] || die "manifest has no project.name"
|
|
101
103
|
[ -n "$PRINCIPAL" ] || die "manifest has no to.principal"
|
|
102
104
|
|
|
103
|
-
|
|
105
|
+
STAGE="$(jq -r '.project.stage // "prd"' "$MANIFEST")"
|
|
106
|
+
PRD_REL="$(jq -r '.project.prdPath // empty' "$MANIFEST")"
|
|
107
|
+
PRD_ABS="${PRD_REL:+$HQ_ROOT/$PRD_REL}"
|
|
108
|
+
BRAINSTORM_ABS="$HQ_ROOT/companies/$COMPANY/projects/$PROJECT/brainstorm.md"
|
|
104
109
|
|
|
105
110
|
# --- assemble prompt sections from the manifest (single source of truth) -----
|
|
106
111
|
|
|
107
112
|
GOAL="" STATE_LINE="" NEXT_STEPS=""
|
|
108
|
-
|
|
113
|
+
DOSSIER_LINE="Start with the project dossier: \`companies/$COMPANY/projects/$PROJECT/\` now contains the PRD (\`prd.json\`), a full brief (\`README.md\` / the DM's details pane), and the delegation journal."
|
|
114
|
+
if [ "$STAGE" = brainstorm ]; then
|
|
115
|
+
# Brainstorm stage: no PRD, no stories. The next steps are to read the
|
|
116
|
+
# brainstorm and turn it into a PRD with /plan.
|
|
117
|
+
[ -f "$BRAINSTORM_ABS" ] && GOAL="$(hq_brainstorm_summary "$BRAINSTORM_ABS")"
|
|
118
|
+
STATE_LINE="This project is at the brainstorm stage: no PRD yet, no stories. The source of truth is \`companies/$COMPANY/projects/$PROJECT/brainstorm.md\`."
|
|
119
|
+
NEXT_STEPS="1. **Read the brainstorm** — \`brainstorm.md\`, the notes under \`research/\`, and the latest journal entry under \`journal/\`.
|
|
120
|
+
2. **Turn it into a PRD** — run \`/plan $PROJECT\` to write \`prd.json\` with stories.
|
|
121
|
+
3. **Start the first story** — once the PRD exists, \`/run-project $PROJECT\` or \`/execute-task\` picks it up."
|
|
122
|
+
DOSSIER_LINE="Start with the project dossier: \`companies/$COMPANY/projects/$PROJECT/\` contains \`brainstorm.md\`, the \`research/\` notes, the latest journal entry, a full brief (the DM's details pane), and the delegation journal. There is no \`prd.json\` yet."
|
|
123
|
+
elif [ -n "$PRD_ABS" ] && [ -f "$PRD_ABS" ] && jq -e . "$PRD_ABS" >/dev/null 2>&1; then
|
|
109
124
|
GOAL="$(jq -r '.metadata.goal // .description // ""' "$PRD_ABS")"
|
|
110
125
|
TOTAL="$(jq -r '[.userStories // [] | .[]] | length' "$PRD_ABS")"
|
|
111
126
|
DONE="$(jq -r '[.userStories // [] | .[] | select(.passes == true)] | length' "$PRD_ABS")"
|
|
@@ -195,7 +210,7 @@ trap 'rm -rf "$STAGE"; [ "$SEND" -eq 0 ] || rmdir "$LOCK"' EXIT
|
|
|
195
210
|
printf '%s\n' "$GET_LINES"
|
|
196
211
|
echo '```'
|
|
197
212
|
echo
|
|
198
|
-
echo "
|
|
213
|
+
echo "$DOSSIER_LINE"
|
|
199
214
|
echo
|
|
200
215
|
if [ -n "$REPO_SECTION" ]; then
|
|
201
216
|
printf '%s\n' "$REPO_SECTION"
|
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Behavior (mode "transfer"; mode "share" skips every mutation):
|
|
10
10
|
# 1. companies/<co>/board.json — the project's entry gets owner=<recipient>
|
|
11
|
-
# and a bumped updated_at; created (once) if absent
|
|
12
|
-
#
|
|
11
|
+
# and a bumped updated_at; created (once) if absent. A brainstorm-stage
|
|
12
|
+
# entry (status exploring, brainstorm_path set) is matched by
|
|
13
|
+
# brainstorm_path and keeps its status.
|
|
14
|
+
# 2. prd.json metadata gains owner, delegatedFrom, delegatedAt; for a
|
|
15
|
+
# brainstorm-stage project the same three keys land in the
|
|
16
|
+
# brainstorm.md frontmatter (owner, delegated_from, delegated_at)
|
|
13
17
|
# 3. work-mesh: `done` event closes the delegator's in-progress thread and
|
|
14
18
|
# broadcasts the reassignment; silently skipped when the helper is
|
|
15
19
|
# unavailable (local/offline installs) — the transfer still succeeds
|
|
@@ -32,6 +36,9 @@ HQ_ROOT="${HQ_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
|
32
36
|
usage() { sed -n '3,25p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; }
|
|
33
37
|
die() { echo "hq-delegate-transfer: $*" >&2; exit 1; }
|
|
34
38
|
|
|
39
|
+
# shellcheck source=lib/delegate-brainstorm.sh
|
|
40
|
+
. "$SCRIPT_DIR/lib/delegate-brainstorm.sh"
|
|
41
|
+
|
|
35
42
|
command -v jq >/dev/null 2>&1 || die "jq is required but not installed"
|
|
36
43
|
|
|
37
44
|
MANIFEST=""
|
|
@@ -68,12 +75,20 @@ jq -e '.status == "granted"' "$MANIFEST" >/dev/null || die "transfer requires co
|
|
|
68
75
|
NOW="$(jq -r '.ownershipTransferredAt // empty' "$MANIFEST")"
|
|
69
76
|
[ -n "$NOW" ] || NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
70
77
|
TODAY="$(date -u +%Y-%m-%d)"
|
|
71
|
-
|
|
78
|
+
STAGE="$(jq -r '.project.stage // "prd"' "$MANIFEST")"
|
|
79
|
+
PROJECT_REL="companies/$COMPANY/projects/$PROJECT"
|
|
80
|
+
PRD_PATH="$PROJECT_REL/prd.json"
|
|
72
81
|
PRD_ABS="$HQ_ROOT/$PRD_PATH"
|
|
73
|
-
|
|
82
|
+
BRAINSTORM_PATH="$PROJECT_REL/brainstorm.md"
|
|
83
|
+
BRAINSTORM_ABS="$HQ_ROOT/$BRAINSTORM_PATH"
|
|
84
|
+
PROJECT_DIR="$HQ_ROOT/$PROJECT_REL"
|
|
74
85
|
BOARD="$HQ_ROOT/companies/$COMPANY/board.json"
|
|
75
86
|
|
|
76
|
-
|
|
87
|
+
case "$STAGE" in
|
|
88
|
+
prd) [ -f "$PRD_ABS" ] || die "prd not found: $PRD_PATH" ;;
|
|
89
|
+
brainstorm) [ -f "$BRAINSTORM_ABS" ] || die "brainstorm.md not found: $BRAINSTORM_PATH" ;;
|
|
90
|
+
*) die "manifest project.stage is '$STAGE' — expected prd or brainstorm" ;;
|
|
91
|
+
esac
|
|
77
92
|
|
|
78
93
|
# --- 1. board entry: update in place, create once if absent ------------------
|
|
79
94
|
|
|
@@ -83,47 +98,72 @@ else
|
|
|
83
98
|
echo '{"projects": []}' > "$BOARD"
|
|
84
99
|
fi
|
|
85
100
|
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
if [ "$STAGE" = prd ]; then
|
|
102
|
+
TITLE="$(jq -r '.name // empty' "$PRD_ABS")"
|
|
103
|
+
DESC="$(jq -r '.description // ""' "$PRD_ABS" | head -c 300)"
|
|
104
|
+
MATCH_PRD="$PRD_PATH"
|
|
105
|
+
MATCH_BS=""
|
|
106
|
+
NEW_STATUS="in_progress"
|
|
107
|
+
else
|
|
108
|
+
TITLE="$(hq_brainstorm_title "$BRAINSTORM_ABS")"
|
|
109
|
+
DESC="$(hq_brainstorm_summary "$BRAINSTORM_ABS" | head -c 300)"
|
|
110
|
+
MATCH_PRD=""
|
|
111
|
+
MATCH_BS="$BRAINSTORM_PATH"
|
|
112
|
+
NEW_STATUS="exploring"
|
|
113
|
+
fi
|
|
114
|
+
[ -n "$TITLE" ] || TITLE="$PROJECT"
|
|
88
115
|
|
|
116
|
+
# An existing entry is matched by prd_path (PRD stage) or brainstorm_path
|
|
117
|
+
# (brainstorm stage). The match is by path, so a brainstorm entry whose
|
|
118
|
+
# prd_path is null still updates in place and keeps its status.
|
|
89
119
|
TMP_BOARD="$(mktemp)"
|
|
90
120
|
jq \
|
|
91
|
-
--arg prd "$
|
|
121
|
+
--arg prd "$MATCH_PRD" \
|
|
122
|
+
--arg bs "$MATCH_BS" \
|
|
92
123
|
--arg owner "$PRINCIPAL" \
|
|
93
124
|
--arg now "$NOW" \
|
|
94
125
|
--arg title "$TITLE" \
|
|
95
126
|
--arg desc "$DESC" \
|
|
96
127
|
--arg project "$PROJECT" \
|
|
128
|
+
--arg status "$NEW_STATUS" \
|
|
97
129
|
'
|
|
98
|
-
|
|
130
|
+
def matches: ($prd != "" and .prd_path == $prd) or ($bs != "" and .brainstorm_path == $bs);
|
|
131
|
+
if ([.projects // [] | .[] | select(matches)] | length) > 0 then
|
|
99
132
|
.projects = [.projects[] |
|
|
100
|
-
if
|
|
133
|
+
if matches then .owner = $owner | .updated_at = $now else . end]
|
|
101
134
|
else
|
|
102
135
|
.projects = (.projects // []) + [{
|
|
103
136
|
id: ("proj-" + $project),
|
|
104
137
|
title: $title,
|
|
105
138
|
description: $desc,
|
|
106
|
-
status:
|
|
139
|
+
status: $status,
|
|
107
140
|
owner: $owner,
|
|
108
|
-
prd_path: $prd,
|
|
141
|
+
prd_path: (if $prd == "" then null else $prd end),
|
|
109
142
|
created_at: $now,
|
|
110
143
|
updated_at: $now
|
|
111
|
-
}]
|
|
144
|
+
} + (if $bs == "" then {} else {brainstorm_path: $bs} end)]
|
|
112
145
|
end
|
|
113
146
|
| .updated_at = $now
|
|
114
147
|
' "$BOARD" > "$TMP_BOARD" && mv "$TMP_BOARD" "$BOARD"
|
|
115
148
|
|
|
116
|
-
# --- 2.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
149
|
+
# --- 2. ownership on the source of truth -------------------------------------
|
|
150
|
+
# PRD stage: prd.json metadata. Brainstorm stage: brainstorm.md frontmatter.
|
|
151
|
+
|
|
152
|
+
if [ "$STAGE" = prd ]; then
|
|
153
|
+
TMP_PRD="$(mktemp)"
|
|
154
|
+
jq \
|
|
155
|
+
--arg owner "$PRINCIPAL" \
|
|
156
|
+
--arg from "$FROM" \
|
|
157
|
+
--arg now "$NOW" \
|
|
158
|
+
'.metadata.owner = $owner
|
|
159
|
+
| .metadata.delegatedFrom = $from
|
|
160
|
+
| .metadata.delegatedAt = $now' \
|
|
161
|
+
"$PRD_ABS" > "$TMP_PRD" && mv "$TMP_PRD" "$PRD_ABS"
|
|
162
|
+
else
|
|
163
|
+
hq_brainstorm_frontmatter_set "$BRAINSTORM_ABS" owner "$PRINCIPAL"
|
|
164
|
+
hq_brainstorm_frontmatter_set "$BRAINSTORM_ABS" delegated_from "$FROM"
|
|
165
|
+
hq_brainstorm_frontmatter_set "$BRAINSTORM_ABS" delegated_at "$NOW"
|
|
166
|
+
fi
|
|
127
167
|
|
|
128
168
|
# --- 3. work mesh: close the delegator's thread, broadcast reassignment ------
|
|
129
169
|
# Silently tolerated when unavailable (local/offline installs no-op).
|
|
@@ -162,7 +202,11 @@ if ! grep -qF "$DELEGATION_ID" "$JOURNAL_FILE"; then
|
|
|
162
202
|
echo "- From: $FROM"
|
|
163
203
|
echo "- To: $DISPLAY ($PRINCIPAL)"
|
|
164
204
|
echo "- Transferred scope: $SCOPE"
|
|
165
|
-
|
|
205
|
+
if [ "$STAGE" = prd ]; then
|
|
206
|
+
echo "- Local board and PRD ownership updated; Work Mesh ownership is unconfirmed. Existing delegator access is unchanged."
|
|
207
|
+
else
|
|
208
|
+
echo "- Brainstorm-stage project (no PRD yet). Local board and brainstorm.md ownership updated; Work Mesh ownership is unconfirmed. Existing delegator access is unchanged."
|
|
209
|
+
fi
|
|
166
210
|
} >> "$JOURNAL_FILE"
|
|
167
211
|
fi
|
|
168
212
|
|
|
@@ -172,4 +216,8 @@ TMP_MANIFEST="$(mktemp)"
|
|
|
172
216
|
jq --arg now "$NOW" '.ownershipTransferredAt = $now | .workMeshOwnership = "unconfirmed"' "$MANIFEST" > "$TMP_MANIFEST" \
|
|
173
217
|
&& mv "$TMP_MANIFEST" "$MANIFEST"
|
|
174
218
|
|
|
175
|
-
|
|
219
|
+
if [ "$STAGE" = prd ]; then
|
|
220
|
+
echo "hq-delegate-transfer: ownership of '$PROJECT' transferred to $DISPLAY ($PRINCIPAL) — local board, PRD, and journal updated; Work Mesh ownership unconfirmed"
|
|
221
|
+
else
|
|
222
|
+
echo "hq-delegate-transfer: ownership of '$PROJECT' (brainstorm stage) transferred to $DISPLAY ($PRINCIPAL) — local board, brainstorm.md, and journal updated; Work Mesh ownership unconfirmed"
|
|
223
|
+
fi
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
# core/scripts/hq-delegate-verify.sh --dry-run \
|
|
18
18
|
# --company <slug> --project <name> --to <principal> [--mode transfer|share]
|
|
19
19
|
#
|
|
20
|
-
# Prints recipient, mode,
|
|
20
|
+
# Prints recipient, mode, project stage (prd, or brainstorm with the dossier
|
|
21
|
+
# files and the /plan next step), every prefix with its permission, the secret
|
|
21
22
|
# names an .env.schema would grant, repo + branch, and the DM headline that
|
|
22
23
|
# would be sent. Exits 0. Creates nothing under workspace/delegations/ and
|
|
23
24
|
# invokes no mutating command.
|
|
@@ -83,6 +84,14 @@ if [ "$DRY_RUN" -eq 1 ]; then
|
|
|
83
84
|
echo " Mode: $MODE"
|
|
84
85
|
echo " Company: $COMPANY"
|
|
85
86
|
echo " Project: $PROJECT"
|
|
87
|
+
if [ "$(printf '%s' "$PLAN_JSON" | jq -r '.project.stage // "prd"')" = brainstorm ]; then
|
|
88
|
+
echo " Stage: brainstorm — no PRD yet; source of truth is brainstorm.md; recommended next step is /plan $PROJECT"
|
|
89
|
+
echo
|
|
90
|
+
echo " Dossier files that would be published:"
|
|
91
|
+
printf '%s' "$PLAN_JSON" | jq -r '.project.dossier[] | " \(.)"'
|
|
92
|
+
else
|
|
93
|
+
echo " Stage: prd"
|
|
94
|
+
fi
|
|
86
95
|
echo
|
|
87
96
|
echo " Vault grants that would be written:"
|
|
88
97
|
printf '%s' "$PLAN_JSON" | jq -r '.vaultPrefixes[] | " \(.permission)\ton \(.prefix)\t(\(.reason // ""))"'
|