@pareto4/pp-core 1.8.2 → 1.9.0

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.
@@ -0,0 +1,250 @@
1
+ # Architecture Deliverables — arc42-lite generation (D-13/D-14/D-15/D-16)
2
+
3
+ > **Lazily-loaded.** Read this file from `pp-core/workflows/discovery-to-build.md`
4
+ > at Step 5h, once all sessions are synthesized (after the `_pp-handoff.md`
5
+ > synthesis and the `.planning/PROJECT.md` / `REQUIREMENTS.md` / `ROADMAP.md`
6
+ > generation steps have run). It is not `@`-imported and is not measured by
7
+ > `tests/workflow-size-budget.test.cjs` (`listWorkflowStems()` is
8
+ > non-recursive) — this is the sanctioned progressive-disclosure escape hatch
9
+ > for a DEFAULT-tier workflow file, not a workaround.
10
+ >
11
+ > Purpose: answers Finaps finding #9 — the client gets a real architect-grade
12
+ > handoff, not just process state. This step is factored on purpose so it can
13
+ > be re-run standalone later if Discovery sessions change, without touching
14
+ > the main `discovery-to-build.md` flow.
15
+
16
+ ## What this step produces
17
+
18
+ Two deliverables, both written under the client project's `docs/` tree —
19
+ **never** under `.planning/`, which stays process-state only:
20
+
21
+ 1. `docs/architecture/architecture.md` — one arc42-lite document with four
22
+ sections: Introduction & Goals, Context, Containers, Data Model.
23
+ 2. `docs/architecture/adr/NNNN-title.md` — one file per row of Session 5's
24
+ Architecture Decision Records table.
25
+
26
+ ## Source of truth
27
+
28
+ All content comes from `.planning/discovery/session-05.md` (Session 5:
29
+ Application & Integration Architecture) plus the following facts already
30
+ captured elsewhere in the synthesis:
31
+
32
+ - **Introduction & Goals** — `.planning/PROJECT.md`'s core problem statement
33
+ (from Session 1) plus Session 5's stated architecture goals/constraints.
34
+ - **Context (C4Context)** — Session 5's Integration Points Map plus Session 8
35
+ (external systems, direction, protocol) for `System_Ext`/`Rel` actors.
36
+ - **Containers (C4Container)** — Session 5's Component Inventory and
37
+ Technology Stack table for `Container`/`ContainerDb` nodes.
38
+ - **Data Model (erDiagram)** — Session 5's Domain Model / Entity Register
39
+ table (D-16: entity, key attributes, relationships, owner module).
40
+ - **ADRs** — Session 5's Architecture Decision Records table
41
+ (`ADR ID | Title | Context | Decision | Consequences | Status`).
42
+
43
+ Never invent facts that are not present in the session content. If a Session
44
+ 5 table is missing or a row is empty, note the gap in the relevant section
45
+ (`[not captured in Discovery]`) rather than fabricating architecture detail.
46
+
47
+ ## Path safety (extends T-04-03)
48
+
49
+ The same discipline `discovery-sprint.md` already applies to
50
+ `.planning/discovery/` writes applies here to `docs/architecture/` writes:
51
+
52
+ - All write targets MUST be relative paths under `docs/architecture/`
53
+ (`docs/architecture/architecture.md`, `docs/architecture/adr/NNNN-title.md`).
54
+ - Never write to an absolute path.
55
+ - Never write to a path containing `..`.
56
+ - The only read target for this step is `.planning/discovery/session-05.md`
57
+ (plus the already-loaded synthesis context) — never read or write outside
58
+ `docs/architecture/` and `.planning/discovery/`.
59
+
60
+ ## Idempotent overwrite handling
61
+
62
+ Before writing, check whether `docs/architecture/` already exists (a prior
63
+ partial run, or a client's hand-authored architecture doc). Follow the same
64
+ "replace an existing block if present, otherwise append" precedent already
65
+ used for the CLAUDE.md injection in `discovery-to-build.md` Step 6b:
66
+
67
+ - If `docs/architecture/architecture.md` exists and contains a section with
68
+ the exact heading being generated (e.g. `## Context`), replace that
69
+ section's content in place; leave any other client-authored sections
70
+ untouched.
71
+ - If the file exists but has no matching section, append the new section at
72
+ the end.
73
+ - If the file does not exist, create it fresh with all four sections.
74
+ - Before overwriting any existing, non-empty `docs/architecture/` content,
75
+ surface a short confirmation summarizing what will change (section names
76
+ being replaced, ADR files being added) rather than silently overwriting —
77
+ mirror the `AskUserQuestion` confirmation shape already used elsewhere in
78
+ `discovery-to-build.md` (Step 4's handoff review prompt).
79
+ - Apply the same per-file idempotency to each `docs/architecture/adr/NNNN-title.md`:
80
+ if a file for that ADR's title already exists, treat it as already
81
+ generated and skip re-writing it rather than duplicating content.
82
+
83
+ ## Generating `docs/architecture/architecture.md`
84
+
85
+ Write (or patch) the file with exactly these four arc42-lite sections, in
86
+ this order. Each section header below is the literal heading text to emit.
87
+
88
+ ### Section 1: Introduction & Goals
89
+
90
+ Prose paragraph combining `.planning/PROJECT.md`'s core problem statement
91
+ with any architecture goals/constraints captured in Session 5 (performance
92
+ targets, scalability expectations, build vs. buy decisions, non-negotiable
93
+ constraints). Keep it factual — summarize, do not add architectural opinions
94
+ that were not stated during Discovery.
95
+
96
+ ### Section 2: Context
97
+
98
+ One paragraph of prose describing the system's boundary and the actors that
99
+ interact with it, followed by a Mermaid `C4Context` block built from Session
100
+ 5's Integration Points Map and Session 8's Integrations table. Map each
101
+ external system in Session 8 to a `System_Ext` node, the product itself to a
102
+ `System` node, and each user-facing actor from Session 5/Session 1 to a
103
+ `Person` node. Connect them with `Rel` lines carrying the protocol from
104
+ Session 8's table where known.
105
+
106
+ Example shape (fill every placeholder from actual captured facts — never
107
+ leave a literal `[System Name]` placeholder in the generated output):
108
+
109
+ ```mermaid
110
+ C4Context
111
+ Person(client, "Client User", "Uses the system")
112
+ System(system, "[System Name]", "[one-line description]")
113
+ System_Ext(ext, "[External System]", "[integration purpose]")
114
+ Rel(client, system, "Uses")
115
+ Rel(system, ext, "Integrates with", "REST/webhook")
116
+ ```
117
+
118
+ ### Section 3: Containers
119
+
120
+ One paragraph of prose describing the system's major runtime containers,
121
+ followed by a Mermaid `C4Container` block built from Session 5's Component
122
+ Inventory and Technology Stack table. Map each component to a `Container`
123
+ node (or `ContainerDb` for data stores), naming the technology from the
124
+ Technology Stack table as the container's tech label. Connect containers with
125
+ `Rel` lines describing the call/data relationship.
126
+
127
+ Example shape (fill every placeholder from actual captured facts):
128
+
129
+ ```mermaid
130
+ C4Container
131
+ Container(web, "Web App", "[frontend tech]", "[responsibility]")
132
+ Container(api, "API", "[backend tech]", "[responsibility]")
133
+ ContainerDb(db, "Database", "[db tech]", "[responsibility]")
134
+ Rel(web, api, "Calls", "HTTPS/REST")
135
+ Rel(api, db, "Reads/writes")
136
+ ```
137
+
138
+ ### Section 4: Data Model
139
+
140
+ One paragraph of prose summarizing the domain model, followed by a Mermaid
141
+ `erDiagram` block built directly from Session 5's Domain Model / Entity
142
+ Register table (D-16). Emit one entity per register row and one relationship
143
+ line per captured relationship; use the register's stated cardinality where
144
+ given, otherwise default to the most conservative reading (`||--o{`) and
145
+ note the assumption in prose.
146
+
147
+ Example shape (fill every placeholder from actual captured facts):
148
+
149
+ ```mermaid
150
+ erDiagram
151
+ ORDER ||--|{ LINE_ITEM : contains
152
+ CUSTOMER ||--o{ ORDER : places
153
+ ```
154
+
155
+ ### Rendering honesty note (mandatory, Pitfall 4)
156
+
157
+ Immediately after the Data Model section (or as a closing note at the end of
158
+ the document), include one prose line stating plainly:
159
+
160
+ > C4 diagrams (`C4Context`, `C4Container`) render on GitLab and Obsidian
161
+ > natively. GitHub's bundled Mermaid renderer does not yet include the C4
162
+ > extension, so these blocks display as a readable code fence rather than a
163
+ > rendered diagram there — the `erDiagram` block renders on all three
164
+ > platforms. This is a platform limitation, not a defect in this document.
165
+
166
+ Do not claim uniform rendering across GitHub/GitLab/Obsidian anywhere else in
167
+ the generated document. All diagram source in this file is embedded Mermaid
168
+ text — no PlantUML, no committed image exports (D-15).
169
+
170
+ ## Generating `docs/architecture/adr/NNNN-title.md`
171
+
172
+ For each row of Session 5's Architecture Decision Records table, write one
173
+ file at `docs/architecture/adr/NNNN-title.md`, where `NNNN` is a
174
+ zero-padded, sequential number starting at `0001` (following pp-core's own
175
+ `docs/adr/NNNN-title.md` numbering precedent — do not reuse pp-core's own
176
+ ADR numbers, this is a separate sequence in the client's `docs/` tree) and
177
+ `title` is the ADR's title, slugified (lowercase, spaces to hyphens, no
178
+ punctuation).
179
+
180
+ Each file follows the exact header shape of pp-core's own
181
+ `docs/adr/0011-review-default-reviewers.md`: a `# Title` line, a
182
+ `- **Status:**` / `- **Date:**` field block, prose Context, then
183
+ `## Decision`, `## Consequences`, `## References` sections.
184
+
185
+ Field mapping from the Session 5 ADR table row to the file:
186
+
187
+ | ADR table column | File field |
188
+ |-------------------|------------|
189
+ | Title | `# {Title}` (H1 heading) |
190
+ | Status | `- **Status:** {Status}` — default to `Accepted` if the row leaves Status blank; use the row's stated value otherwise |
191
+ | (session completion date) | `- **Date:** {session-05 completion date}` — read from `.planning/discovery/session-05.md`'s own status/completion metadata, or `.planning/discovery/_progress.md`'s Session 5 `Completed` column |
192
+ | Context | Prose paragraph expanding the Context column's content (do not just paste the table cell verbatim if it is telegraphic — write it as a short paragraph while preserving the original meaning) |
193
+ | Decision | `## Decision` section body |
194
+ | Consequences | `## Consequences` section body |
195
+ | — | `## References` section: `- Source: \`.planning/discovery/session-05.md\` — Architecture Decision Records table, {ADR ID}` |
196
+
197
+ Example shape:
198
+
199
+ ```markdown
200
+ # [ADR Title from Session 5 table]
201
+
202
+ - **Status:** Accepted
203
+ - **Date:** [session completion date]
204
+
205
+ [Context column content, expanded to prose]
206
+
207
+ ## Decision
208
+
209
+ [Decision column content]
210
+
211
+ ## Consequences
212
+
213
+ [Consequences column content]
214
+
215
+ ## References
216
+
217
+ - Source: `.planning/discovery/session-05.md` — Architecture Decision Records table, [ADR ID]
218
+ ```
219
+
220
+ If Session 5's ADR table has no rows (architecture decisions not yet
221
+ captured), skip `docs/architecture/adr/` entirely rather than generating
222
+ empty placeholder files, and note the gap in `architecture.md`'s
223
+ Introduction & Goals section.
224
+
225
+ ## Completing the step
226
+
227
+ 1. Create `docs/architecture/` and `docs/architecture/adr/` directories as
228
+ needed (they do not exist in a fresh client project).
229
+ 2. Write `docs/architecture/architecture.md` per the section instructions
230
+ above, applying the idempotent replace-or-append rule.
231
+ 3. Write one `docs/architecture/adr/NNNN-title.md` per Session 5 ADR row,
232
+ skipping any that already exist (idempotent).
233
+ 4. Commit the generated files. This step is the SOLE committer of
234
+ `docs/architecture/` — `discovery-to-build.md` Step 5h does not commit
235
+ these files itself, so this block inlines the canonical `pp_run`
236
+ resolver unconditionally, exactly as every other workflow file does.
237
+ The resolver is idempotent, so redefining `pp_run` when a caller has
238
+ already defined it is harmless:
239
+
240
+ ```bash
241
+ _PP_SHIM_NAME="pp-tools.cjs"; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; pp_run() { "$PP_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
242
+
243
+ FILES="docs/architecture/architecture.md"
244
+ ls docs/architecture/adr/*.md >/dev/null 2>&1 && FILES="$FILES docs/architecture/adr/*.md"
245
+ pp_run commit "docs: generate architect-grade handoff (arc42-lite architecture + ADRs)" --files $FILES
246
+ ```
247
+
248
+ 5. Return control to `discovery-to-build.md`'s Step 5h caller — this step
249
+ does not prompt for further user input beyond the overwrite confirmation
250
+ above (if triggered).