hstack 0.4.0 → 0.5.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.
- package/CHANGELOG.md +41 -0
- package/README.md +56 -14
- package/VERSION +1 -1
- package/package.json +1 -1
- package/template/.claude/agents/app-architect.md +154 -0
- package/template/.claude/agents/data-architect.md +147 -0
- package/template/.claude/agents/product-discovery.md +165 -0
- package/template/.claude/agents/stack-architect.md +137 -0
- package/template/.claude/skills/hstack-app-architecture/SKILL.md +109 -0
- package/template/.claude/skills/{hstack-init → hstack-brownfield-init}/SKILL.md +17 -11
- package/template/.claude/skills/hstack-data-architecture/SKILL.md +100 -0
- package/template/.claude/skills/hstack-greenfield-init/SKILL.md +150 -0
- package/template/.claude/skills/hstack-product-discovery/SKILL.md +101 -0
- package/template/.claude/skills/hstack-scaffold/SKILL.md +140 -0
- package/template/.claude/skills/hstack-stack-decide/SKILL.md +108 -0
- package/template/CLAUDE.md +20 -6
- package/template/templates/app-architecture.md +102 -0
- package/template/templates/bootstrap.md +105 -0
- package/template/templates/data-architecture.md +147 -13
- package/template/templates/discovery/brainstorm.md +69 -0
- package/template/templates/discovery/forcing-questions.md +76 -0
- package/template/templates/discovery/project-brief.md +53 -0
- package/template/templates/product-brief.md +56 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: app-architecture
|
|
3
|
+
type: app-architecture
|
|
4
|
+
status: draft # draft | current | needs-refresh | archived
|
|
5
|
+
owner: <git-handle>
|
|
6
|
+
derived-from: [product-brief, data-architecture]
|
|
7
|
+
downstream: [threat-model, hardening-checklist, tech-stack, module-spec/*]
|
|
8
|
+
created: <YYYY-MM-DD>
|
|
9
|
+
updated: <YYYY-MM-DD>
|
|
10
|
+
schema-version: 1
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Module Map
|
|
14
|
+
|
|
15
|
+
_The set of modules and what each owns. Each module must trace to either a persona-named action in `product-brief.md` or to a logical clustering of entities from `data-architecture.md`. Orphan modules (no trace) halt the section with the drift challenge._
|
|
16
|
+
|
|
17
|
+
**Modules**
|
|
18
|
+
|
|
19
|
+
| Module | Owns | Traces to |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| <module-name> | <one-line statement of what this module owns> | <persona-action OR entity-cluster> |
|
|
22
|
+
|
|
23
|
+
**Drift challenge answered**
|
|
24
|
+
|
|
25
|
+
_"Does any module here own state a persona never interacts with, OR does any persona's journey traverse modules in a way the boundaries don't support?"_
|
|
26
|
+
|
|
27
|
+
## 2. Agent Orchestration Model
|
|
28
|
+
|
|
29
|
+
_How LLM calls compose. Which modules call the LLM, what each call returns, how downstream modules consume the output. Tool boundaries. Where prompts live. For an AI-native SaaS, this is the meaty section — implicit "the LLM handles it" answers are rejected; named call sites and downstream consumers are required._
|
|
30
|
+
|
|
31
|
+
**LLM call sites**
|
|
32
|
+
|
|
33
|
+
| Call site (module.fn) | Trigger | Model | Returns | Downstream consumer |
|
|
34
|
+
| --- | --- | --- | --- | --- |
|
|
35
|
+
| <module>.<function> | <what triggers this call> | <model identifier> | <structured-output schema or one-line description> | <module that consumes> |
|
|
36
|
+
|
|
37
|
+
**Tool boundaries**
|
|
38
|
+
|
|
39
|
+
_Which tools each call site can invoke, and which are explicitly out-of-bounds. Names the kill switches for LLM-driven actions._
|
|
40
|
+
|
|
41
|
+
**Drift challenge answered**
|
|
42
|
+
|
|
43
|
+
_"Does any LLM call site bypass the tool boundaries declared, or have an unnamed retry / fallback path?"_
|
|
44
|
+
|
|
45
|
+
## 3. Deterministic-vs-LLM Split
|
|
46
|
+
|
|
47
|
+
_Per user-facing flow, the per-step decision: code or prompt, with a rationale that ties to a measurable property (determinism, cost, latency, capability). Implicit "AI handles it end-to-end" answers are rejected; per-step declaration is mandatory._
|
|
48
|
+
|
|
49
|
+
**Flow tables**
|
|
50
|
+
|
|
51
|
+
For each flow named in the brief's persona vignettes:
|
|
52
|
+
|
|
53
|
+
### Flow: <flow-name>
|
|
54
|
+
|
|
55
|
+
_From <persona>'s Tuesday-morning workflow._
|
|
56
|
+
|
|
57
|
+
| Step | Mechanism | Rationale (ties to measurable property) |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| 1. <step description> | deterministic \| llm | <one sentence — property: determinism \| cost \| latency \| capability> |
|
|
60
|
+
| 2. ... | ... | ... |
|
|
61
|
+
|
|
62
|
+
**Drift challenge answered**
|
|
63
|
+
|
|
64
|
+
_"Does any flow have a step where the mechanism is undeclared, or a rationale that doesn't tie to a measurable property?"_
|
|
65
|
+
|
|
66
|
+
## 4. State-Ownership Map
|
|
67
|
+
|
|
68
|
+
_Where conversation state lives, workspace state lives, ephemeral / browser-session state lives. Each state class names its owning module from Section 1 and its persistence layer from `data-architecture.md`. State without an owning module triggers the drift challenge._
|
|
69
|
+
|
|
70
|
+
**State classes**
|
|
71
|
+
|
|
72
|
+
| State class | Owning module | Persistence layer | Lifetime |
|
|
73
|
+
| --- | --- | --- | --- |
|
|
74
|
+
| <e.g., conversation history> | <module from §1> | <entity from data-architecture OR "ephemeral"> | <durable \| session \| request> |
|
|
75
|
+
|
|
76
|
+
**Drift challenge answered**
|
|
77
|
+
|
|
78
|
+
_"Does any state class lack an owning module from Section 1?"_
|
|
79
|
+
|
|
80
|
+
## 5. Surface Boundaries
|
|
81
|
+
|
|
82
|
+
_What the project's `surfaces` enum contains. The canonical floor is `[ui, api, agent, db, auth, infra]`; projects may add (e.g., `payments`) or omit (e.g., no `auth` if relying on a host system). Each declared surface is mapped to at least one module from Section 1._
|
|
83
|
+
|
|
84
|
+
**Surface declaration**
|
|
85
|
+
|
|
86
|
+
| Surface | In v1? | Modules carrying this surface | Deferred to |
|
|
87
|
+
| --- | --- | --- | --- |
|
|
88
|
+
| ui | yes/no | <module names> | <v2 \| later \| N/A> |
|
|
89
|
+
| api | ... | ... | ... |
|
|
90
|
+
| agent | ... | ... | ... |
|
|
91
|
+
| db | ... | ... | ... |
|
|
92
|
+
| auth | ... | ... | ... |
|
|
93
|
+
| infra | ... | ... | ... |
|
|
94
|
+
| <custom> | ... | ... | ... |
|
|
95
|
+
|
|
96
|
+
**Drift challenge answered**
|
|
97
|
+
|
|
98
|
+
_"Does any surface in the enum have no module from Section 1 mapped to it?"_
|
|
99
|
+
|
|
100
|
+
## Coherence check (end-of-atom)
|
|
101
|
+
|
|
102
|
+
_When the atom reaches terminal state, the agent re-runs all five drift challenges to ensure section-targeted edits did not silently break other sections. The coherence check is fatal — any unanswered challenge halts the commit._
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: <YYYY-MM-bootstrap> # canonical bootstrap change-id; one per project
|
|
3
|
+
type: change-spec # bootstrap is a change-spec variant, not a new type
|
|
4
|
+
status: draft
|
|
5
|
+
owner: <git-handle>
|
|
6
|
+
area: bootstrap # the no-story carve-out for the bootstrap variant; SP-09 satisfied via this
|
|
7
|
+
surfaces: [infra] # bootstrap is infra-only; UI / agent / api work begins post-bootstrap
|
|
8
|
+
user-stories: [] # bootstrap has no user-story; the area: bootstrap field satisfies SP-09 as a third carve-out alongside internal-tooling and enables
|
|
9
|
+
related-spec: bootstrap # equals area
|
|
10
|
+
related-adrs: [] # populated with every stack ADR from Phase 4
|
|
11
|
+
creates-tech-debt: []
|
|
12
|
+
resolves-tech-debt: []
|
|
13
|
+
parent-change: null
|
|
14
|
+
children: []
|
|
15
|
+
revisits-change: []
|
|
16
|
+
internal-tooling: false # bootstrap is NOT internal-tooling — the code ships on the user path
|
|
17
|
+
enables: [] # bootstrap implicitly enables every downstream change-spec; the explicit list would be degenerate, so we leave it empty and rely on area: bootstrap as the SP-09 carve-out
|
|
18
|
+
enabled-by: []
|
|
19
|
+
trivial: false
|
|
20
|
+
in-scope: [] # explicit file enumeration — NOT ["."]
|
|
21
|
+
out-of-scope: [] # required, may be empty
|
|
22
|
+
threat-model-delta: true # bootstrap always touches threat-model (auth posture, RLS defaults, secret handling)
|
|
23
|
+
created: <YYYY-MM-DD>
|
|
24
|
+
updated: <YYYY-MM-DD>
|
|
25
|
+
schema-version: 1
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Problem
|
|
29
|
+
|
|
30
|
+
_What this change is doing: standing up the repo from empty. One paragraph._
|
|
31
|
+
|
|
32
|
+
This change scaffolds the project from an empty repository to a bootable, tested, and ready-to-ship state. It is the only change in the project's history with `area: bootstrap`; subsequent changes target real modules.
|
|
33
|
+
|
|
34
|
+
## Current Behavior
|
|
35
|
+
|
|
36
|
+
_N/A for bootstrap — there is no current state. Repository is empty (or contains only `hstack/` after greenfield-init Phases 1–5)._
|
|
37
|
+
|
|
38
|
+
- N/A.
|
|
39
|
+
|
|
40
|
+
## Target Behavior
|
|
41
|
+
|
|
42
|
+
_What shipping looks like, observably. Bootstrap's targets are bootability and gate-passing, not feature behavior._
|
|
43
|
+
|
|
44
|
+
- The repository builds: `<configured-build-command>` exits 0.
|
|
45
|
+
- The repository's test suite runs and passes: `<configured-test-command>` exits 0.
|
|
46
|
+
- Every module declared in `app-architecture.md` Section 1 has a corresponding source directory with the minimal shell required for the module-spec to be reverse-engineered post-merge.
|
|
47
|
+
- The initial migration sequence sketched in `data-architecture.md` Section 5 lands as actual `.sql` files under `supabase/migrations/` (or the chosen DB's migration directory) in the order: schema → RLS → pgvector → seeds.
|
|
48
|
+
- CI runs the canonical commands from `ci-cd.md` and the gates pass.
|
|
49
|
+
|
|
50
|
+
## Acceptance Criteria
|
|
51
|
+
|
|
52
|
+
_GIVEN / WHEN / THEN. Bootstrap's acceptance is gate-based, not user-flow-based._
|
|
53
|
+
|
|
54
|
+
GIVEN the repository at HEAD
|
|
55
|
+
WHEN the engineer runs `<configured-build-command>` and `<configured-test-command>`
|
|
56
|
+
THEN both exit 0 and no test is skipped.
|
|
57
|
+
|
|
58
|
+
GIVEN the database after `m_0002_rls_policies.sql` has run
|
|
59
|
+
WHEN any tenant-scoped table is queried without setting `app.<tenant-column>`
|
|
60
|
+
THEN the query returns zero rows (RLS enforced from line zero).
|
|
61
|
+
|
|
62
|
+
GIVEN the repository at HEAD
|
|
63
|
+
WHEN a downstream engineer runs `/hstack:module-spec <module>` for any module from `app-architecture.md` Section 1
|
|
64
|
+
THEN the Skill finds a target directory to reverse-engineer against and does not halt on missing source.
|
|
65
|
+
|
|
66
|
+
## Invariants
|
|
67
|
+
|
|
68
|
+
_Three or more bullets per SP-04. Bootstrap's invariants name the foundational guarantees that must survive every future change._
|
|
69
|
+
|
|
70
|
+
- **RLS-enforced from line zero.** Every tenant-scoped table created by `m_0001_initial_schema.sql` has its RLS policy applied in `m_0002_rls_policies.sql` BEFORE any data lands. The migration ordering is a contract, not a convenience.
|
|
71
|
+
- **Module boundaries match the declaration.** Every module in `app-architecture.md` Section 1 has a corresponding source directory; no module is silently dropped or renamed during scaffold. Future changes cannot add modules without updating the declaration first.
|
|
72
|
+
- **Stack ADRs are authoritative.** Every choice in this scaffold (framework, DB client, auth integration, hosting deploy file, observability wiring) traces to an ADR from Phase 4. No silent stack divergence.
|
|
73
|
+
|
|
74
|
+
## Scope Boundaries
|
|
75
|
+
|
|
76
|
+
_Pointer to `in-scope` and `out-of-scope` frontmatter arrays. Bootstrap's `in-scope` is an explicit enumeration of every file being created. Wildcards are permitted but the engineer must list every top-level destination explicitly so the implementer's scope-lock does meaningful work._
|
|
77
|
+
|
|
78
|
+
The `in-scope` enumeration is the contract: the implementer creates exactly the listed files and no others. Files appearing in the final scaffold but not in `in-scope` indicate a scaffold-spec gap and require a scope amendment.
|
|
79
|
+
|
|
80
|
+
## Surfaces
|
|
81
|
+
|
|
82
|
+
_Pointer to `surfaces` frontmatter. Bootstrap is `[infra]` only — UI / agent / api / db / auth surface work begins with the first feature change-spec after bootstrap merges._
|
|
83
|
+
|
|
84
|
+
- **infra**: build configuration, dependency manifest, CI workflow, DB migration tooling setup, deployment configuration, observability wiring.
|
|
85
|
+
|
|
86
|
+
UI work begins post-bootstrap; the scaffold ships only the minimum shell (layout, theme tokens) needed for the build to pass.
|
|
87
|
+
|
|
88
|
+
## Linked Stories and Personas
|
|
89
|
+
|
|
90
|
+
_N/A — bootstrap has `user-stories: []` and satisfies SP-09 via `area: bootstrap`. The bootstrap variant is a third carve-out alongside `internal-tooling: true` (Category A) and `enables: [...]` (Category B); `area: bootstrap` is mutually exclusive with both. See kernel addendum for the rule._
|
|
91
|
+
|
|
92
|
+
## Related ADRs and Tech-Debt
|
|
93
|
+
|
|
94
|
+
_Every stack ADR from Phase 4 (greenfield-init) goes here as a pointer with one-sentence justification. Examples:_
|
|
95
|
+
|
|
96
|
+
- ADR-NNNN — Stack defaults adopted: <list>. Adopted as the project foundation.
|
|
97
|
+
- ADR-NNNN+1 — Observability stack: PostHog + Sentry. Configured in this scaffold.
|
|
98
|
+
|
|
99
|
+
## Resolves Tech-Debt
|
|
100
|
+
|
|
101
|
+
_N/A — bootstrap creates the project; it does not resolve prior tech-debt._
|
|
102
|
+
|
|
103
|
+
## Open Questions
|
|
104
|
+
|
|
105
|
+
_Populated when status moves from draft to ready-to-plan. Bootstrap-specific examples: "Do we lint the migration SQL via sqlfluff or via Supabase's built-in lint?" "Do we vendor a UI component library in the initial commit or wait for the first UI feature?"_
|
|
@@ -1,35 +1,169 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: data-architecture
|
|
3
3
|
type: data-architecture
|
|
4
|
-
status:
|
|
4
|
+
status: draft # draft | current | needs-refresh | archived
|
|
5
5
|
owner: <git-handle>
|
|
6
|
+
assumes-database: postgres # explicit so Phase 4 stack-decide can flag drift
|
|
6
7
|
schema-snapshot-date: <YYYY-MM-DD>
|
|
7
8
|
rag-architecture-version: 1
|
|
9
|
+
derived-from: [product-brief]
|
|
10
|
+
downstream: [app-architecture, threat-model, hardening-checklist, module-spec/*]
|
|
8
11
|
created: <YYYY-MM-DD>
|
|
9
12
|
updated: <YYYY-MM-DD>
|
|
10
13
|
schema-version: 1
|
|
11
14
|
---
|
|
12
15
|
|
|
13
|
-
##
|
|
16
|
+
## 1. Tenancy Model
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
_The load-bearing question. The data-architect refuses to advance past this section until the tenant definition is concrete and ties to a persona in the brief._
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
**Pattern** — one of:
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
- **A. Tenant = the customer organization** — single workspace per paying customer; users inside share visibility.
|
|
23
|
+
- **B. Tenant = a sub-team within the customer organization** — multi-workspace per customer.
|
|
24
|
+
- **C. Tenant = the individual user** — workspace-per-user.
|
|
25
|
+
- **D. Custom** — document the pattern explicitly and explain why none of A/B/C fit.
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
**Rationale**
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
_One sentence tying the chosen pattern to the persona in `product-brief.md`. Example: "Pattern A because Maya (Director of CS) shares visibility with her 6 reps; sub-team isolation isn't requested by any v1 persona."_
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
**Tenant identifier**
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
_The column name and type used to scope every tenant-scoped table (`workspace_id uuid`, `org_id text`, etc.). All downstream RLS predicates reference this column._
|
|
28
34
|
|
|
29
|
-
##
|
|
35
|
+
## 2. Entity Graph
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
_The set of entities and their relationships. Each entity must trace to either a persona-named action in `product-brief.md` or to the tenancy model from Section 1. Orphan entities halt with the drift challenge._
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
**Entities**
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
| Entity | Owns | Traces to | Relationships |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| <entity-name> | <one-line statement> | <persona-action OR tenancy-derived> | <FK refs> |
|
|
44
|
+
|
|
45
|
+
**Conventions** (subsumed from prior template)
|
|
46
|
+
|
|
47
|
+
_Naming (snake_case, plural tables), timestamps (`created_at`, `updated_at` on every table, ISO 8601), soft-deletes vs hard-deletes per entity, UUID vs serial choice with rationale, audit fields if any._
|
|
48
|
+
|
|
49
|
+
**Drift challenge answered**
|
|
50
|
+
|
|
51
|
+
_"Does any entity here have no trace to a persona or feature in the brief? Name it."_
|
|
52
|
+
|
|
53
|
+
## 3. RLS Posture
|
|
54
|
+
|
|
55
|
+
_Per-table policy sketch. Every entity from Section 2 falls into one of two categories — no third._
|
|
56
|
+
|
|
57
|
+
**Tenant-scoped tables**
|
|
58
|
+
|
|
59
|
+
| Table | Policy predicate sketch | Notes |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| <table> | `<tenant-column> = current_setting('app.<tenant-column>')::uuid` | <e.g., RPC-only writes, no direct INSERT> |
|
|
62
|
+
|
|
63
|
+
**Intentionally global tables**
|
|
64
|
+
|
|
65
|
+
| Table | Why no RLS | Rationale |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| <table> | <lookup data \| public reference \| ...> | <one sentence> |
|
|
68
|
+
|
|
69
|
+
**Drift challenge answered**
|
|
70
|
+
|
|
71
|
+
_"Does any tenant-scoped entity have an RLS policy that the chosen tenancy model wouldn't enforce? Name it."_
|
|
72
|
+
|
|
73
|
+
## 4. RAG / pgvector
|
|
74
|
+
|
|
75
|
+
_Whether v1 uses embeddings, what entity carries them, which embedding model, the tenant-scoped retrieval RPC signature. If v1 does not use RAG, mark this section `not-in-v1` with rationale and skip ahead._
|
|
76
|
+
|
|
77
|
+
**Use in v1**
|
|
78
|
+
|
|
79
|
+
_`yes` or `not-in-v1`. If not-in-v1, justify in one sentence (e.g., "v1 ships change-awareness without semantic retrieval; pgvector is a v2 layer")._
|
|
80
|
+
|
|
81
|
+
**Embedding-bearing entities**
|
|
82
|
+
|
|
83
|
+
| Entity | Embedding column | Model | Dimensions | Tenant-scoped retrieval RPC |
|
|
84
|
+
| --- | --- | --- | --- | --- |
|
|
85
|
+
| <entity> | <column name> | <e.g., openai:text-embedding-3-small> | <e.g., 1536> | <RPC signature with tenant predicate> |
|
|
86
|
+
|
|
87
|
+
**Tenant-scoping requirement**
|
|
88
|
+
|
|
89
|
+
_Every retrieval RPC MUST filter by the Section 1 tenant identifier. The drift challenge verifies this._
|
|
90
|
+
|
|
91
|
+
**Drift challenge answered**
|
|
92
|
+
|
|
93
|
+
_"Does any embedding-bearing entity have a retrieval RPC that bypasses tenant scoping? Name it."_
|
|
94
|
+
|
|
95
|
+
## 5. Migration Sketches
|
|
96
|
+
|
|
97
|
+
_Postgres-dialect DDL sketches for the initial migrations, with `-- TODO: confirm` markers on uncertain parts. The implementer reads these during bootstrap and writes the actual `.sql` files; the sketches are intent, not executable. Typical ordering: schema → RLS → pgvector. Data must never land before RLS._
|
|
98
|
+
|
|
99
|
+
**Migration sequence**
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
m_0001_initial_schema.sql -- Section 2 entities, tables + FKs, no policies yet
|
|
103
|
+
m_0002_rls_policies.sql -- Section 3 policies, applied per tenant-scoped table
|
|
104
|
+
m_0003_pgvector_setup.sql -- Section 4 RPCs and indexes (skip if not-in-v1)
|
|
105
|
+
m_0004_seed_global_data.sql -- Section 3 intentionally-global table seeds (optional)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Sketch — m_0001_initial_schema.sql**
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
-- TODO: confirm UUID extension is enabled
|
|
112
|
+
create extension if not exists "uuid-ossp";
|
|
113
|
+
|
|
114
|
+
-- TODO: confirm column types and FK ON DELETE behaviors per entity
|
|
115
|
+
create table <entity> (
|
|
116
|
+
id uuid primary key default uuid_generate_v4(),
|
|
117
|
+
<tenant-column> uuid not null references <tenant-table>(id) on delete cascade,
|
|
118
|
+
-- entity-specific columns
|
|
119
|
+
created_at timestamptz not null default now(),
|
|
120
|
+
updated_at timestamptz not null default now()
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Sketch — m_0002_rls_policies.sql**
|
|
125
|
+
|
|
126
|
+
```sql
|
|
127
|
+
-- TODO: confirm session-variable name matches app convention
|
|
128
|
+
alter table <entity> enable row level security;
|
|
129
|
+
|
|
130
|
+
create policy <entity>_tenant_isolation on <entity>
|
|
131
|
+
for all
|
|
132
|
+
using (<tenant-column> = current_setting('app.<tenant-column>')::uuid);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Sketch — m_0003_pgvector_setup.sql** (skip if Section 4 is `not-in-v1`)
|
|
136
|
+
|
|
137
|
+
```sql
|
|
138
|
+
-- TODO: confirm pgvector version compatible with embedding model dimensions
|
|
139
|
+
create extension if not exists vector;
|
|
140
|
+
|
|
141
|
+
alter table <embedding-bearing-entity>
|
|
142
|
+
add column embedding vector(<dimensions>);
|
|
143
|
+
|
|
144
|
+
create index on <embedding-bearing-entity> using ivfflat (embedding vector_cosine_ops);
|
|
145
|
+
|
|
146
|
+
-- TODO: confirm RPC signature and tenant predicate
|
|
147
|
+
create or replace function nearest_<entity>(query_embedding vector, k int, p_<tenant-column> uuid)
|
|
148
|
+
returns table (id uuid, score float4)
|
|
149
|
+
language sql stable
|
|
150
|
+
as $$
|
|
151
|
+
select id, embedding <=> query_embedding as score
|
|
152
|
+
from <embedding-bearing-entity>
|
|
153
|
+
where <tenant-column> = p_<tenant-column>
|
|
154
|
+
order by score
|
|
155
|
+
limit k;
|
|
156
|
+
$$;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Data lifecycle** (subsumed from prior template)
|
|
160
|
+
|
|
161
|
+
_Retention windows per entity (e.g., "interactions: 18 months hot, archived after"), archival path, deletion guarantees (hard-delete vs soft-delete vs anonymize-and-retain). Lifecycle differences per tenant tier (free vs paid) noted here._
|
|
162
|
+
|
|
163
|
+
**Drift challenge answered**
|
|
164
|
+
|
|
165
|
+
_"Does any migration in the sketch sequence land data before its RLS policy? Name it."_
|
|
166
|
+
|
|
167
|
+
## Coherence check (end-of-atom)
|
|
168
|
+
|
|
169
|
+
_When the atom reaches terminal state, the agent re-runs all five drift challenges. Any unanswered challenge halts the commit._
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: discovery-brainstorm
|
|
3
|
+
type: discovery-technique
|
|
4
|
+
technique: brainstorm
|
|
5
|
+
applies-to: product-brief
|
|
6
|
+
schema-version: 1
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Brainstorm — technique script
|
|
10
|
+
|
|
11
|
+
This is the prompt sequence `product-discovery` runs in Brainstorm mode. Use this when the engineer has a problem domain but no concrete concept. The mode is **facilitated ideation** — the agent acts as coach pulling ideas out via structured exercises, not generating ideas for the engineer.
|
|
12
|
+
|
|
13
|
+
## Pre-flight check
|
|
14
|
+
|
|
15
|
+
Before entering the script, the agent confirms:
|
|
16
|
+
|
|
17
|
+
- The engineer can name the problem domain in one sentence ("CS teams losing follow-up context") even if the solution is undefined.
|
|
18
|
+
- The engineer has 60+ minutes available. Brainstorm runs multi-round; cutting it short produces shallow output.
|
|
19
|
+
|
|
20
|
+
## Round 1 — SCAMPER pass on the problem statement
|
|
21
|
+
|
|
22
|
+
For each SCAMPER lens, ask the engineer one question and write their answer:
|
|
23
|
+
|
|
24
|
+
- **Substitute** — "What current workflow could be substituted away?"
|
|
25
|
+
- **Combine** — "What two things, combined, would create new value here?"
|
|
26
|
+
- **Adapt** — "What other industry has solved a similar pain — what did they do?"
|
|
27
|
+
- **Modify** — "What single behavior, if amplified, would solve most of the pain?"
|
|
28
|
+
- **Put to another use** — "What's a non-obvious user who has this same pain?"
|
|
29
|
+
- **Eliminate** — "What part of the current workflow could disappear entirely?"
|
|
30
|
+
- **Reverse** — "What if the user did the opposite of what they do today?"
|
|
31
|
+
|
|
32
|
+
Answers land as raw text in a scratch section. Do NOT synthesize yet.
|
|
33
|
+
|
|
34
|
+
## Round 2 — Reverse brainstorming
|
|
35
|
+
|
|
36
|
+
Ask: "How would you make this problem **worse** for the user? List five ways."
|
|
37
|
+
|
|
38
|
+
The engineer's answers reveal the load-bearing failure modes — the things the product must NOT do. These directly seed the **Explicitly NOT** section of the brief.
|
|
39
|
+
|
|
40
|
+
## Round 3 — Six Thinking Hats on the strongest thread
|
|
41
|
+
|
|
42
|
+
The agent picks the strongest 1–2 threads from Rounds 1 and 2 (the threads with the most concrete user actions named) and proposes them back. For each thread, walk the hats:
|
|
43
|
+
|
|
44
|
+
- **White hat (facts)** — "What do we observably know about this user's workflow today?"
|
|
45
|
+
- **Red hat (intuition)** — "What feels right or wrong about this thread, gut-check?"
|
|
46
|
+
- **Black hat (caution)** — "What's the strongest case against this thread?"
|
|
47
|
+
- **Yellow hat (optimism)** — "What's the strongest case for this thread?"
|
|
48
|
+
- **Green hat (creativity)** — "What's the wildest version of this thread?"
|
|
49
|
+
- **Blue hat (process)** — "Is this the right thread to deepen, or should we drop it?"
|
|
50
|
+
|
|
51
|
+
After the Six Hats run, the engineer either commits to a thread or returns to Round 1 with a sharper problem statement.
|
|
52
|
+
|
|
53
|
+
## Round 4 — Synthesis
|
|
54
|
+
|
|
55
|
+
The agent proposes a one-paragraph synthesis of the strongest thread, framed as: "Here is what I heard you converge on. Concept: X. Underlying pain: Y. Target user: Z." The engineer confirms or revises.
|
|
56
|
+
|
|
57
|
+
On confirmation, the agent transitions to the **product-brief.md** template and walks the sections, using the synthesis as the seed for Underlying Pain, Target User, and Value Proposition.
|
|
58
|
+
|
|
59
|
+
## Required reframes (mandatory before brief lands)
|
|
60
|
+
|
|
61
|
+
These run during the section walk of `product-brief.md`, regardless of which Brainstorm round produced the synthesis:
|
|
62
|
+
|
|
63
|
+
- "Who specifically pays for this?" — concrete persona required.
|
|
64
|
+
- "What's the smallest useful wedge?" — minimum shippable surface required.
|
|
65
|
+
- "What would you have to believe to be wrong about this?" — falsifiability required.
|
|
66
|
+
|
|
67
|
+
## Park-and-resume
|
|
68
|
+
|
|
69
|
+
The agent surfaces a parking offer after each Round commits. If the engineer parks, the round's output is persisted in `hstack/.session-state/<session-id>.yaml`. Resume picks up at the next round.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: discovery-forcing-questions
|
|
3
|
+
type: discovery-technique
|
|
4
|
+
technique: forcing-questions
|
|
5
|
+
applies-to: product-brief
|
|
6
|
+
schema-version: 1
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Forcing-questions — technique script
|
|
10
|
+
|
|
11
|
+
This is the prompt sequence `product-discovery` runs in Forcing-Questions mode. Use this when the engineer has a concept but it's vague or oversold — the technique probes claims and reframes the underlying pain. Pattern derived from YC-partner forcing questions and the Gstack approach.
|
|
12
|
+
|
|
13
|
+
The agent's operating mode in this technique is **reframe-driven probing**: every claim the engineer makes triggers a reframe. The engineer's job is to defend the claim or accept the reframe. The brief converges only when the claims survive the probes.
|
|
14
|
+
|
|
15
|
+
## Pre-flight check
|
|
16
|
+
|
|
17
|
+
Before entering the script, the agent confirms:
|
|
18
|
+
|
|
19
|
+
- The engineer has a concept they can state in one or two sentences.
|
|
20
|
+
- The engineer is open to being wrong about their concept. The technique surfaces reframes that may invalidate the founder's first articulation; founders who can't tolerate that should pick Project-Brief mode instead.
|
|
21
|
+
|
|
22
|
+
## Probe 1 — The 10-star reframe
|
|
23
|
+
|
|
24
|
+
Ask: "What's the 10-star product hiding inside this request? What you described is 5-star; what's the 10-star version that solves the actual underlying pain?"
|
|
25
|
+
|
|
26
|
+
The engineer's first answer is often a feature embellishment ("...with AI"). Re-ask: "That's a feature. What's the **underlying** pain — what does the user fail to do today that the 10-star product would make trivial?"
|
|
27
|
+
|
|
28
|
+
The reframe sticks when the engineer names a pain that is one level deeper than the original concept. The brief's **Underlying Pain** section seeds from this answer.
|
|
29
|
+
|
|
30
|
+
## Probe 2 — The smallest useful wedge
|
|
31
|
+
|
|
32
|
+
Ask: "What's the smallest useful wedge? If you shipped one slice of the 10-star product and nothing else, what's the one slice that would still get a real user to pay?"
|
|
33
|
+
|
|
34
|
+
The engineer's first answer is usually too large. Re-ask: "Cut it in half. What's left when you remove every feature that isn't load-bearing for the slice you just described?"
|
|
35
|
+
|
|
36
|
+
The reframe sticks when the engineer names a wedge that can be built by one engineer in a quarter and that, alone, would justify a real user's payment. The brief's **Smallest Useful Wedge** section seeds from this answer.
|
|
37
|
+
|
|
38
|
+
## Probe 3 — The named user
|
|
39
|
+
|
|
40
|
+
Ask: "Who specifically pays for this? Name a real person — first name, role, company-size band, and the workflow this product enters on a Tuesday morning."
|
|
41
|
+
|
|
42
|
+
The engineer's first answer is often a role abstraction ("Directors of CS"). Re-ask: "Pick one specific Director of CS you've talked to. What's her name, what's her company's name, and what does she do on a Tuesday morning that this product changes?"
|
|
43
|
+
|
|
44
|
+
If the engineer cannot name a specific person, halt and surface: "The named-user constraint isn't met. We can either (a) park here and you go talk to three real users this week, or (b) acknowledge in the brief that this is anchored on a hypothetical user, which makes everything downstream a gamble." Founders sometimes choose (b); the brief records the choice in **Open Risks**.
|
|
45
|
+
|
|
46
|
+
The brief's **Target User** section seeds from this answer.
|
|
47
|
+
|
|
48
|
+
## Probe 4 — The falsification probe
|
|
49
|
+
|
|
50
|
+
Ask: "What would you have to believe to be wrong about this concept? Name the belief and the evidence that would falsify it."
|
|
51
|
+
|
|
52
|
+
The engineer's first answer is often "users won't like it" — too vague. Re-ask: "Be specific. What's a load-bearing assumption about the user, the market, or the technology that, if wrong, kills the product? And what experiment would tell you if it's wrong?"
|
|
53
|
+
|
|
54
|
+
The reframe sticks when the engineer names a falsifiable belief plus an experiment that would resolve it within weeks. The brief's **Open Risks** section seeds from this answer.
|
|
55
|
+
|
|
56
|
+
## Probe 5 — The scope-reduction question
|
|
57
|
+
|
|
58
|
+
Ask: "If you had to ship in 8 weeks with one engineer, what would you cut? Walk me through the cut list."
|
|
59
|
+
|
|
60
|
+
The engineer's cuts reveal what they think is load-bearing vs. embellishment. The brief's **Explicitly NOT** section seeds from the cut list — the things the engineer would cut are exactly the things v1 explicitly does NOT do.
|
|
61
|
+
|
|
62
|
+
## Probe 6 — The reframe-staleness check
|
|
63
|
+
|
|
64
|
+
After Probes 1–5, the agent surfaces: "The brief is converging on [X]. Your starting concept was [Y]. Any external documents (Notion pages, pitch deck, README) still naming [Y]? Those are now stale. Cleanup checklist:"
|
|
65
|
+
|
|
66
|
+
The engineer either confirms the docs are stale and lists them for cleanup, or pushes back ("no, Y is still the framing") — in which case the agent re-runs Probe 1 to reconcile.
|
|
67
|
+
|
|
68
|
+
## Synthesis
|
|
69
|
+
|
|
70
|
+
After all six probes, the agent proposes a one-paragraph synthesis: "Here is what survived the probes. Concept: [refined X]. Underlying pain: [Probe 1 answer]. Smallest wedge: [Probe 2 answer]. Named user: [Probe 3 answer]. Falsification: [Probe 4 answer]." The engineer confirms or revises.
|
|
71
|
+
|
|
72
|
+
On confirmation, the agent transitions to the **product-brief.md** template and walks the sections. The probe answers seed the corresponding brief sections; the Forcing-Prompt Answers section records the probes verbatim as evidence the technique ran.
|
|
73
|
+
|
|
74
|
+
## Park-and-resume
|
|
75
|
+
|
|
76
|
+
The agent surfaces a parking offer after each probe commits. If the engineer parks, the probe's output is persisted in `hstack/.session-state/<session-id>.yaml`. Resume picks up at the next probe.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: discovery-project-brief
|
|
3
|
+
type: discovery-technique
|
|
4
|
+
technique: project-brief
|
|
5
|
+
applies-to: product-brief
|
|
6
|
+
schema-version: 1
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Project-brief — technique script
|
|
10
|
+
|
|
11
|
+
This is the prompt sequence `product-discovery` runs in Project-Brief mode. Use this when the engineer is already concrete about the product and just needs structure to land the brief artifact. This is the **lightest-touch** technique — collaborative, structured, no heavy reframes — but the three required forcing prompts still fire.
|
|
12
|
+
|
|
13
|
+
The agent's operating mode in this technique is **structured walk**: the engineer's existing thinking is the proposal layer; the agent prompts section by section, confirms or revises, lands the brief.
|
|
14
|
+
|
|
15
|
+
## Pre-flight check
|
|
16
|
+
|
|
17
|
+
Before entering the script, the agent confirms:
|
|
18
|
+
|
|
19
|
+
- The engineer can state the concept in two or three sentences without hedging.
|
|
20
|
+
- The engineer can name a specific real user (not a role abstraction).
|
|
21
|
+
- The engineer can name a concrete success metric.
|
|
22
|
+
|
|
23
|
+
If any of the three fails the concreteness floor, the agent halts and suggests switching to Forcing-Questions mode — the engineer's thinking is too vague for Project-Brief mode, which assumes concreteness.
|
|
24
|
+
|
|
25
|
+
## Section walk
|
|
26
|
+
|
|
27
|
+
The agent walks `product-brief.md` section by section. For each section, the agent asks one open question, accepts the engineer's answer, surfaces one clarifying re-ask if the answer is vague, then commits the section.
|
|
28
|
+
|
|
29
|
+
1. **Underlying Pain** — "Describe what the user struggles with today, in concrete terms. A Tuesday-morning workflow vignette is the unit of clarity."
|
|
30
|
+
2. **Target User** — "Name a specific real user. First name, role, company-size band, the tools they use, the workflow this product enters."
|
|
31
|
+
3. **Value Proposition** — "What outcome does this product deliver to the named user? Outcome, not features."
|
|
32
|
+
4. **Smallest Useful Wedge** — "What's the minimum shippable surface that delivers real value? Would the named user still pay for the wedge alone?"
|
|
33
|
+
5. **Success Criteria** — "What measurable, time-bound outcome tells you v1 worked? Concrete number, concrete window."
|
|
34
|
+
6. **Explicitly NOT** — "What is this product NOT? Two bullets minimum. What population it does NOT serve, what workflows it does NOT enter, what features it does NOT include."
|
|
35
|
+
7. **Open Risks** — "What could make this product fail? Name at least one belief that, if wrong, kills the product."
|
|
36
|
+
|
|
37
|
+
## Required forcing prompts
|
|
38
|
+
|
|
39
|
+
Even in this lightest-touch mode, the three required forcing prompts MUST run before the brief can land. They run after the section walk completes:
|
|
40
|
+
|
|
41
|
+
- "Who specifically pays for this?" — re-prompt the engineer to defend the Target User against the buyer question. The Target User and the buyer may be different (Maya the Director of CS uses it; her CFO pays for it). Both must be named.
|
|
42
|
+
- "What's the smallest useful wedge?" — re-prompt the engineer to defend the wedge against the "would the user pay for the wedge alone?" question. Yes required.
|
|
43
|
+
- "What would you have to believe to be wrong about this?" — re-prompt for a falsifiable belief + experiment to resolve it. Names the load-bearing risk.
|
|
44
|
+
|
|
45
|
+
These three answers are logged in the brief's **Forcing-Prompt Answers** section as evidence the probes ran. The engineer cannot skip them; the technique downgrades to Forcing-Questions mode mid-session if the engineer refuses any of the three.
|
|
46
|
+
|
|
47
|
+
## Synthesis
|
|
48
|
+
|
|
49
|
+
There is no explicit synthesis step in Project-Brief mode — the section walk IS the synthesis. The brief lands at `status: current` when the last section commits and the three forcing prompts are answered.
|
|
50
|
+
|
|
51
|
+
## Park-and-resume
|
|
52
|
+
|
|
53
|
+
The agent surfaces a parking offer after each section commits. If the engineer parks, the section's output is on disk and `hstack/.session-state/<session-id>.yaml` records the next-section pointer. Resume picks up at the next section.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: product-brief
|
|
3
|
+
type: product-brief
|
|
4
|
+
status: draft # draft | current | needs-refresh | archived
|
|
5
|
+
owner: <git-handle>
|
|
6
|
+
technique-used: <brainstorm | forcing-questions | project-brief>
|
|
7
|
+
derived-from: [] # always empty — product-brief is Phase 1, no upstream
|
|
8
|
+
downstream: [vision, mvp-scope, personas, glossary]
|
|
9
|
+
created: <YYYY-MM-DD>
|
|
10
|
+
updated: <YYYY-MM-DD>
|
|
11
|
+
schema-version: 1
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Underlying Pain
|
|
15
|
+
|
|
16
|
+
_What the user actually struggles with on a Tuesday morning, in concrete terms. Not "users want X" — "this specific person spends 20 minutes doing Y and misses Z." One paragraph, 4–8 sentences. The forcing-prompt reframes should have moved this away from the founder's first articulation; the result lands here._
|
|
17
|
+
|
|
18
|
+
## Target User
|
|
19
|
+
|
|
20
|
+
_A named, specific user (not "users", not "customers") with a workday vignette. Includes role, company-size band, tools they use today, the workflow this product enters. If multiple personas matter for v1, name them all — but each must pass the concreteness floor._
|
|
21
|
+
|
|
22
|
+
## Value Proposition
|
|
23
|
+
|
|
24
|
+
_What this product delivers to the named user, in their language. One paragraph. Distinct from features — this is the outcome they buy._
|
|
25
|
+
|
|
26
|
+
## Smallest Useful Wedge
|
|
27
|
+
|
|
28
|
+
_The minimum shippable surface that delivers real value to the named user. Forcing-prompt answer: "Would the named user still pay for this if you shipped only the wedge and nothing else?" Yes required; if no, the wedge is too large or the user is wrong._
|
|
29
|
+
|
|
30
|
+
## Success Criteria
|
|
31
|
+
|
|
32
|
+
_Concrete, measurable, time-bound. "Maya saves 10 minutes per QBR prep within 4 weeks of activation" not "users love it." At least one criterion; more is better._
|
|
33
|
+
|
|
34
|
+
## Explicitly NOT
|
|
35
|
+
|
|
36
|
+
_Two-bullet minimum. The boundaries of the product. What this is NOT, what it does NOT do, what populations it does NOT serve. Required, never optional — over-broad scoping is the #1 product failure mode._
|
|
37
|
+
|
|
38
|
+
-
|
|
39
|
+
-
|
|
40
|
+
|
|
41
|
+
## Open Risks
|
|
42
|
+
|
|
43
|
+
_The things that could make this product fail. The forcing-prompt "What would you have to believe to be wrong about this?" answer lands here. At least one risk; more is better._
|
|
44
|
+
|
|
45
|
+
## Forcing-Prompt Answers
|
|
46
|
+
|
|
47
|
+
_Evidence that the three required reframes ran. Logged inline as confirmation the probes were not skipped._
|
|
48
|
+
|
|
49
|
+
**Who specifically pays for this?**
|
|
50
|
+
_The named user / buyer plus the rationale ("Director of CS, because retention is their KPI")._
|
|
51
|
+
|
|
52
|
+
**What's the smallest useful wedge?**
|
|
53
|
+
_The wedge plus the rationale ("Change-awareness alone, because the rest of the workflow is downstream of it")._
|
|
54
|
+
|
|
55
|
+
**What would you have to believe to be wrong about this?**
|
|
56
|
+
_The belief plus the falsifiability angle ("That CS reps will trust LLM-generated summaries — wrong if they require human-curated context")._
|