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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 8 additions & 65 deletions drizzle/0004_gaps_node_kind_reference.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
CREATE TABLE `elicitation_gaps_new` (
DROP TABLE IF EXISTS `elicitation_gaps_new`;
--> statement-breakpoint
DROP TABLE IF EXISTS `elicitation_gaps`;
--> statement-breakpoint
DROP TABLE IF EXISTS `elicitation_backlog`;
--> statement-breakpoint
Comment on lines +1 to +6
CREATE TABLE `elicitation_gaps` (
Comment on lines +1 to +7
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`spec_id` integer NOT NULL,
`refers_to` text NOT NULL,
Expand All @@ -17,69 +23,6 @@ CREATE TABLE `elicitation_gaps_new` (
`created_at_lsn` integer NOT NULL,
`disposition_set_at_lsn` integer,
FOREIGN KEY (`spec_id`) REFERENCES `specs`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`arose_from_gap_id`) REFERENCES `elicitation_gaps_new`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`arose_from_gap_id`) REFERENCES `elicitation_gaps`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`resolved_by_node_id`) REFERENCES `nodes`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
INSERT INTO `elicitation_gaps_new` (
`id`,
`spec_id`,
`refers_to`,
`question`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration drops existing gap rows

Medium Severity

The rewritten 0004 migration drops elicitation_gaps and recreates it empty, with no INSERT … SELECT from the prior table. Workspaces already at 0003 (or any DB with populated gap rows) lose all elicitation gap state on migrate; existing specs are not re-seeded, so capability-readiness and gap-driven behavior can silently diverge from pre-upgrade data.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 405b892. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration drops existing gap rows

High Severity

Replacing 0004 with DROP TABLE plus a fresh CREATE TABLE for elicitation_gaps removes every row that existed under the 0003 name-column schema. There is no INSERT/SELECT or other backfill, and createSpec only seeds new specs, so existing workspaces lose capability-readiness and elicitation state after migrate.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0442270. Configure here.

Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
`rationale`,
`disposition`,
`basis`,
`readiness_band`,
`predicate_kind`,
`predicate`,
`importance`,
`plane_affinity`,
`lens_affinity`,
`arose_from_gap_id`,
`resolved_by_node_id`,
`created_at_lsn`,
`disposition_set_at_lsn`
)
SELECT
`id`,
`spec_id`,
CASE
WHEN `name` = 'domain' THEN 'context'
WHEN `name` = 'protagonist' THEN 'thesis'
WHEN `name` = 'pain_pull' THEN 'thesis'
WHEN `name` = 'constraint' THEN 'constraint'
WHEN `name` = 'value' THEN 'goal'
WHEN `name` = 'context_of_use' THEN 'context'
WHEN `name` = 'success_sketch' THEN 'criterion'
WHEN `name` = 'solution_boundary' THEN 'constraint'
ELSE COALESCE(json_extract(`predicate`, '$.nodeKind'), json_extract(`predicate`, '$.subjectKind'), 'context')
END,
CASE
WHEN `name` = 'domain' THEN 'What kind of thing is this, and what domain or environment does it live in?'
WHEN `name` = 'protagonist' THEN 'Who is this for?'
WHEN `name` = 'pain_pull' THEN 'What pull or pain makes this worth doing?'
WHEN `name` = 'constraint' THEN 'What binding constraints, non-goals, or boundaries already shape the work?'
WHEN `name` = 'value' THEN 'What outcome or value should this create?'
WHEN `name` = 'context_of_use' THEN 'When, where, or under what conditions will it be used?'
WHEN `name` = 'success_sketch' THEN 'How will we recognize success or good enough?'
WHEN `name` = 'solution_boundary' THEN 'What is explicitly out of scope or off the table?'
ELSE CASE WHEN trim(`name`) = '' THEN 'What needs to be clarified here?' ELSE trim(`name`) END
END,
`rationale`,
`disposition`,
`basis`,
`readiness_band`,
`predicate_kind`,
`predicate`,
`importance`,
`plane_affinity`,
`lens_affinity`,
`arose_from_gap_id`,
`resolved_by_node_id`,
`created_at_lsn`,
`disposition_set_at_lsn`
FROM `elicitation_gaps`;
--> statement-breakpoint
DROP TABLE `elicitation_gaps`;
--> statement-breakpoint
ALTER TABLE `elicitation_gaps_new` RENAME TO `elicitation_gaps`;
50 changes: 27 additions & 23 deletions memory/PLAN.md

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions memory/SPEC.md

Large diffs are not rendered by default.

249 changes: 249 additions & 0 deletions memory/cards/renderer-golden-coverage--render-stage-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
# Renderer Golden Coverage Chain

Frontier: renderer-golden-coverage
Status: active
Mode: chain
Created: 2026-06-11

## Orientation

- Seam: RENDER-stage reusable lossy text under `src/renderers/` plus the sketch → lock → formalize oracle loop that keeps LLM-facing wording from drifting silently.
- Frontier: `renderer-golden-coverage`, now the next sequenced trio work after `projection-shape-coverage` closed on this branch.
- Current truth in-tree:
- already locked: `graph/graph-slice`, `graph/node-neighborhood`, `session/runtime-frame`
- uncovered likely `●`: `session/transcript`, `workspace/workspace-state`, `exchanges/request-*`, `exchanges/present-question`, `exchanges/present-options`, `exchanges/present-review-set`
- disposition still needs an honest call: `workspace/workspace-context` is named as a consumer seam in `src/session/README.md`, but current code-path grep finds no live caller
- explicit `○` / topology stubs that should stay out unless their seam activates: `graph/commit-result.ts`, `graph/reconciliation-needs.ts`, `exchanges/present-candidates.ts`
- Known doc drift to retire first:
- `memory/PLAN.md` previously spoke as if `src/scripts/render-preview.ts` / `npm run render` already existed; they do not.
- `memory/CROSS_CUT_PLAN.md` still describes file-snapshot locks as net-new even though `graph/` and `session/runtime-frame` now use `toMatchFileSnapshot`.
- Main risk: snapshotting dead or single-owner rows for symmetry, or overbuilding a preview framework before the ledger names exactly which renderers still matter.
- Cross-cutting obligations: preserve D52-L (`renderers/` stays free of adapter/transport imports); keep goldens co-located with renderer tests under `src/renderers/**/__previews__/`; keep `○` stubs untouched; preserve the human eyeball step before lock.

Posture: proving. Card 1 retires authority drift; Cards 2-5 cash the now-legible coverage closures.

## Dependency Sketch

```text
Card 1 renderer ledger + preview-loop authority
├─ unlocks Cards 2-5 by making the required rows and sketch path explicit
└─ may delete/demote workspace-context instead of snapshotting it

Cards 2-5 are independent after Card 1
├─ Card 2 workspace rows
├─ Card 3 session transcript row
├─ Card 4 exchange request-family rows
└─ Card 5 exchange present-family rows + frontier close
```

## Card 1 - Close the renderer ledger and preview-loop authority

### Objective

Before adding any more goldens, close the authority gap around what counts as a required renderer row and what the actual sketch path is. This card should make later cards mechanical rather than rediscovery-heavy.

### Light-card cold-start reads

- `memory/PLAN.md` — frontier: `renderer-golden-coverage`
- `memory/CROSS_CUT_PLAN.md` — §Renderer feedback loops
- `src/renderers/README.md`
- existing locked tests: `src/renderers/graph/previews.test.ts`, `src/renderers/session/runtime-frame.test.ts`
- `src/session/README.md` — current `workspace-context` consumer claims
- `package.json`

### Acceptance Criteria

✓ `src/renderers/README.md` carries a closed renderer ledger with one row per current renderer, including required/deferred/stub disposition and current oracle status.

✓ The ledger makes an explicit call on `workspace/workspace-context`: keep-and-cover only if it still owns a real consumer seam; otherwise demote or retire it.

✓ `memory/PLAN.md` and any touched cross-cut notes stop claiming a preview harness already exists when it does not.

✓ This card chooses one honest sketch path for the rest of the frontier: either materialize a minimal shared preview harness (`src/scripts/render-preview.ts` + `npm run render`) or explicitly narrow the frontier to test-local preview generation. Later cards should not reopen that choice.

### Out of scope / guardrails

- Do not snapshot every uncovered renderer in this card.
- Do not keep `workspace-context` merely to preserve directory symmetry.
- Do not invent a preview DSL or generalized renderer framework.

### Expected touched paths (tentative)

```text
memory/
├── PLAN.md ~
└── CROSS_CUT_PLAN.md ~?
package.json ~?
src/renderers/
└── README.md ~
src/scripts/
└── render-preview.ts +?
```

## Card 2 - Close the workspace renderer rows honestly

### Objective

Close the `workspace/` rows without pretending both existing files are equally real. `workspace-state` is clearly load-bearing; `workspace-context` needs an explicit keep-or-retire decision before it gets any snapshot surface.

### Light-card cold-start reads

- Card 1 output in `src/renderers/README.md`
- `src/renderers/workspace/workspace-state.ts`
- `src/renderers/workspace/workspace-state.test.ts`
- `src/renderers/workspace/workspace-context.ts`
- `src/session/README.md`
- active callers such as `src/app/brunch.ts`

### Acceptance Criteria

✓ `workspace-state` has co-located preview/golden coverage plus semantic invariants over live status variants and the absence of retired chrome/readiness fields.

✓ If `workspace-context` survives Card 1 as a required row, it gets its own co-located preview/golden over both `cwd_inventory` and `workspace_overview`; if not, it is deleted or demoted and the docs agree.

✓ The renderer ledger marks the workspace rows closed after this card.

### Out of scope / guardrails

- No new workspace projection or adapter layer.
- No reintroduction of retired `phase`, `chatMode`, or persisted readiness concepts.
- Do not widen session read shapes merely to make renderer tests easier.

### Expected touched paths (tentative)

```text
src/renderers/workspace/
├── workspace-state.ts ~?
├── workspace-state.test.ts ~
├── workspace-context.ts ~|-
└── __previews__/ +?
src/renderers/README.md ~
src/session/README.md ~?
src/app/brunch.ts ?
```

## Card 3 - Move transcript markdown locking into renderer home

### Objective

Give `renderers/session/transcript.ts` its own co-located golden and invariants so the renderer home owns transcript wording, while `session/session-transcript.ts` keeps only the wrapper/parsing proof it actually owns.

### Light-card cold-start reads

- Card 1 output in `src/renderers/README.md`
- `src/renderers/session/transcript.ts`
- `src/session/session-transcript.ts`
- `src/session/session-transcript.test.ts`
- `src/projections/session/transcript-context.ts`

### Acceptance Criteria

✓ `src/renderers/session/transcript.ts` has a co-located preview/golden built from a mixed transcript fixture covering user content, assistant text, generic tool results, structured exchange tool results, and omitted non-text blocks.

✓ The renderer-level test owns the text-shape lock; higher-level `session/session-transcript.*` tests keep only wrapper behavior that the renderer test does not prove.

✓ The ledger marks `session/transcript` covered while leaving `session/runtime-frame` as already covered.

### Out of scope / guardrails

- No new transcript projection fields or session-manager behavior changes.
- Do not widen transcript rendering to include thinking/toolCall/image blocks.
- Do not move transcript parsing ownership out of `session/`.

### Expected touched paths (tentative)

```text
src/renderers/session/
├── transcript.ts ~?
├── transcript.test.ts +
└── __previews__/ +
src/session/
├── session-transcript.ts ?
└── session-transcript.test.ts ~
src/renderers/README.md ~
```

## Card 4 - Lock the request-side exchange renderer family

### Objective

Lock the request-response renderers as one family because they all render the same terminal union shape (`cancelled | unavailable | answered`) and share the same failure modes: comment quoting, markdown escaping, and branch-specific copy drift.

### Light-card cold-start reads

- Card 1 output in `src/renderers/README.md`
- `src/renderers/exchanges/request-answer.ts`
- `src/renderers/exchanges/request-choice.ts`
- `src/renderers/exchanges/request-choices.ts`
- `src/renderers/exchanges/request-review.ts`
- corresponding `.pi/extensions/exchanges/request-*.ts` callers

### Acceptance Criteria

✓ `request-answer`, `request-choice`, `request-choices`, and `request-review` each have preview/golden coverage for answered and non-answered branches.

✓ Invariants cover the semantic edges snapshots can hide: cancel/unavailable copy, markdown escaping of labels, and quote-block handling for optional comments.

✓ Tests reuse existing domain DTO shapes and do not become a second schema/persistence test suite.

### Out of scope / guardrails

- No exchange-schema or editor-fallback redesign.
- No `present_candidates` work.
- No candidate-capture or generalized-capture symmetry expansion.

### Expected touched paths (tentative)

```text
src/renderers/exchanges/
├── request-answer.ts ~?
├── request-choice.ts ~?
├── request-choices.ts ~?
├── request-review.ts ~?
├── request-family.test.ts +?
└── __previews__/ +
src/renderers/README.md ~
```

## Card 5 - Lock the present-side exchange renderers and close the frontier

### Objective

Close the remaining exchange prompt-side rows, then reconcile the ledger so `renderer-golden-coverage` can hand off cleanly to `prompt-composition-golden-coverage`.

### Light-card cold-start reads

- Card 1 output in `src/renderers/README.md`
- `src/renderers/exchanges/present-question.ts`
- `src/renderers/exchanges/present-options.ts`
- `src/renderers/exchanges/present-review-set.ts`
- corresponding `src/projections/exchanges/*.ts` sources and current structured-exchange tests

### Acceptance Criteria

✓ `present-question`, `present-options`, and `present-review-set` each have co-located preview/golden coverage plus at least one semantic invariant.

✓ The invariants guard the real failure modes: heading/body composition for question; hidden option-id comment retention plus escaping for options; stable entity/edge draft narration for review-set without raw internal refs bleeding through.

✓ `present-candidates` remains explicit `○` / topology stub unless the active codebase now gives it a real consumer.

✓ Final docs mark every `●` row in `src/renderers/README.md` as covered and every `○` row explicit, so the frontier can advance to `prompt-composition-golden-coverage`.

### Out of scope / guardrails

- No new exchange renderer families.
- No prompt-composition work yet.
- Do not reopen the sketch-path choice settled in Card 1.

### Expected touched paths (tentative)

```text
src/renderers/exchanges/
├── present-question.ts ~?
├── present-options.ts ~?
├── present-review-set.ts ~?
├── present-family.test.ts +?
└── __previews__/ +
src/renderers/README.md ~
memory/PLAN.md ~?
```
31 changes: 11 additions & 20 deletions src/.pi/__tests__/context-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,15 @@ describe('context tools', () => {
},
})) as {
content: Array<{ type: 'text'; text: string }>;
details: { mode: 'cwd_inventory'; data: { markdownFiles: Array<{ path: string }> } };
details: { markdownFiles: Array<{ path: string }> };
};

expect(result.content[0]?.text).toContain('[Workspace cwd inventory]');
expect(result.content[0]?.text).toContain('existing .brunch state detected');
expect(result.content[0]?.text).toContain('session-1.jsonl');
expect(result.details.mode).toBe('cwd_inventory');
expect(result.details.data.markdownFiles.map((file) => file.path)).toEqual([
'README.md',
'visible/guide.md',
]);
expect(result.details).not.toHaveProperty('mode');
expect(result.details).not.toHaveProperty('data');
expect(result.details.markdownFiles.map((file) => file.path)).toEqual(['README.md', 'visible/guide.md']);
});

it('read_session_context returns runtime-frame markdown plus typed details', async () => {
Expand Down Expand Up @@ -234,22 +232,17 @@ describe('context tools', () => {
},
})) as {
content: Array<{ type: 'text'; text: string }>;
details: {
mode: 'workspace_overview';
data: { specs: Array<{ title: string }>; sessions: Array<{ turnCount: number }> };
};
details: { specs: Array<{ title: string }>; sessions: Array<{ turnCount: number }> };
};

expect(result.content[0]?.text).toContain('[Workspace overview]');
expect(result.content[0]?.text).toContain('Alpha Grounding');
expect(result.content[0]?.text).toContain('Beta Commitments');
expect(result.content[0]?.text).not.toContain('readiness_grade=');
expect(result.details.mode).toBe('workspace_overview');
expect(result.details.data.specs.map((spec) => spec.title)).toEqual([
'Alpha Grounding',
'Beta Commitments',
]);
expect(result.details.data.sessions.map((session) => session.turnCount)).toEqual([1, 2]);
expect(result.details).not.toHaveProperty('mode');
expect(result.details).not.toHaveProperty('data');
expect(result.details.specs.map((spec) => spec.title)).toEqual(['Alpha Grounding', 'Beta Commitments']);
expect(result.details.sessions.map((session) => session.turnCount)).toEqual([1, 2]);
});

// Authentic oracle: drive the context tools against the faux harness's REAL
Expand Down Expand Up @@ -290,12 +283,10 @@ describe('context tools', () => {
const workspaceResult = (await tools
.get('read_workspace_context')!
.execute('faux-workspace', { mode: 'cwd_inventory' }, undefined, undefined, ctx)) as {
details: { data: { markdownFiles: Array<{ path: string }> } };
details: { markdownFiles: Array<{ path: string }> };
};
// cwd came from the header (the temp workbench), not process.cwd().
expect(workspaceResult.details.data.markdownFiles.map((file) => file.path)).toContain(
'faux-guard-doc.md',
);
expect(workspaceResult.details.markdownFiles.map((file) => file.path)).toContain('faux-guard-doc.md');
} finally {
harness.dispose();
}
Expand Down
Loading
Loading