feat(tools): add gsd_requirement_list/get and gsd_decision_list/get - #1613
Open
pimmink wants to merge 1 commit into
Open
feat(tools): add gsd_requirement_list/get and gsd_decision_list/get#1613pimmink wants to merge 1 commit into
pimmink wants to merge 1 commit into
Conversation
…pen-gsd#1608) Adds four read-only canonical store tools that give agents authoritative DB access without parsing stale markdown projections. ## New tools ### gsd_requirement_list - Reads from the canonical `requirements` table - Filters: class (JS-level), status (DB-level), milestoneId (DB-level) - Limit: default 200, hard cap 500 - Returns: { count, requirements[] } — full Requirement rows ### gsd_requirement_get - Point-lookup by stable R### ID - Typed error responses: not_found | db_unavailable - Returns null for superseded requirements (active-only by design) ### gsd_decision_list - Reads from the canonical `memories` table (ADR-013 Stage 3) - Filters: scope (exact match), milestoneId (substring on when_context) - includeSuperseded: false (default) → queryDecisionsFromMemories includeSuperseded: true → getAllDecisionsFromMemories + JS filter - Limit: default 200, hard cap 500 - Returns: { count, decisions[] } — full Decision rows ### gsd_decision_get - Point-lookup by stable D### ID from memories table - includeSuperseded: true to retrieve superseded decisions - Typed error: not_found (absent/tombstoned/superseded) | db_unavailable - Tombstone-aware: deleted: true rows return not_found ## Context-store additions Two new exported helpers in `context-store.ts`: - getRequirementById(id): Requirement | null - getDecisionById(id, includeSuperseded?): Decision | null Both degrade gracefully (return null) when DB unavailable. Never throw. Decision lookup reads `json_extract(structured_fields, '$.sourceDecisionId')` consistent with ADR-013 Stage 3 memory-backed authority. ## Tests (16 passing, node:test + node:assert/strict) - JS-level filter correctness (class, scope, milestoneId, superseded) - Hard-cap limit logic (200 default, 500 ceiling) - Tombstone detection (deleted: true → null) - superseded_by guard with includeSuperseded toggle - Decision shape reconstruction from structured_fields - Malformed row skipping (invalid JSON, missing sourceDecisionId) - Error response shape contracts (not_found vs db_unavailable) AI assistance used (disclosed per CONTRIBUTING.md). Closes open-gsd#1608
Contributor
🟠 PR Risk Report — HIGH
Affected Systems
File Breakdown
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closes #1608
Agents cannot authoritatively verify what is in the canonical requirement/decision store without parsing Markdown projections (REQUIREMENTS.md, DECISIONS.md). These projections can be stale, missing, or inconsistent — particularly after
gsd migrateor a failed projection regen. Any workflow that callsgsd_requirement_savecannot safely detect duplicates without a full projection parse.Confirmed absent from
bootstrap/db-tools.ts,workflow-tool-executors.ts, andbootstrap/memory-tools.tsin v1.12.0.New tools (4)
gsd_requirement_listReads from the canonical
requirementstable. Returns authoritative live-DB contents, never a stale projection.Inputs:
classstatusactive,validated,deferred, etc.milestoneIdM005)limitOutput:
{ count: number, requirements: Requirement[] }gsd_requirement_getPoint-lookup by stable
R###ID. Returns the fullRequirementrow or a typed error object — never throws, never fabricates.Inputs:
{ id: string }— e.g.R021Output:
Requirementrow, or{ error: not_found | db_unavailable }not_found: ID does not exist or is superseded (active-only)db_unavailable: GSD database could not be openedgsd_decision_listReads from the canonical
memoriestable (ADR-013 Stage 3 — the legacydecisionstable is no longer authoritative). Returns the sameDecisionshape as the existing prompt-inline query layer.Inputs:
scopearchitecture)milestoneIdwhen_context(e.g.M005)includeSupersededfalselimitActive path (
includeSuperseded: false): delegates toqueryDecisionsFromMemories(existing function, already used by the prompt-inline path).Full-chain path (
includeSuperseded: true): delegates togetAllDecisionsFromMemories+ JS-level scope/milestoneId filter.Output:
{ count: number, decisions: Decision[] }gsd_decision_getPoint-lookup by stable
D###ID from the memories table. Tombstone-aware.Inputs:
{ id: string, includeSuperseded?: boolean }Output:
Decisionrow, or{ error: not_found | db_unavailable }not_found: ID is absent, is adeleted: truetombstone, or is superseded (withoutincludeSuperseded: true)db_unavailable: GSD database could not be openedImplementation details
context-store.ts additions
Two new exported helpers:
getRequirementById(id: string): Requirement | nullDirect
SELECT * FROM requirements WHERE id = :id AND superseded_by IS NULL. Degrades gracefully (null) when DB unavailable. Never throws.getDecisionById(id: string, includeSuperseded = false): Decision | nullQueries
json_extract(structured_fields, '$.sourceDecisionId') = :idon the memories table, consistent with ADR-013 Stage 3. Handles tombstone (deleted: true) and superseded guard in the JS loop (same pattern asreadDecisionsFromMemories).db-tools.ts additions
Four tools follow the exact
registerWorkflowToolpattern of existing tools:requirementListExecute/requirementListTool/registerWorkflowTool(pi, requirementListTool)requirementGetExecute/requirementGetTool/registerWorkflowTool(pi, requirementGetTool)decisionListExecute/decisionListTool/registerWorkflowTool(pi, decisionListTool)decisionGetExecute/decisionGetTool/registerWorkflowTool(pi, decisionGetTool)Each tool has:
name,label,description,promptSnippet,promptGuidelines,parameters(TypeBox),execute,renderCall,renderResult.Error responses use structured
detailsobjects ({ error: 'not_found' | 'db_unavailable' }) matching the existinggsd_milestone_statuspattern, so TUIrenderResultcan distinguish warning from error colouring.Tests (16 passing, node:test + node:assert/strict)
Tests cover: JS-level filter correctness, hard-cap limit logic, tombstone detection, superseded_by guard with toggle, Decision shape reconstruction, malformed row skipping, error response shape contracts.
Non-breaking
Zero deletions. No changes to existing tools, query functions, or DB schema. The four new tools are purely additive.
verify:fastpasses clean.Design notes vs original issue
The original issue proposed a two-PR split (requirements first, decisions second) due to the ADR-013 Stage 3 complexity. In practice,
queryDecisionsFromMemoriesandgetAllDecisionsFromMemoriesalready handle the memories-path entirely — no new DB query complexity was needed. A single PR is cleaner.The
gsd_decision_list/gsd_decision_gettools do NOT touch the legacydecisionstable. All reads go through the memories authority path, consistent with ADR-013 Stage 3.AI assistance used (disclosed per CONTRIBUTING.md).
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.