Skip to content

feat(figma): design-to-text mapping resolver (Phase C — T2F.5, T2F.6, T2F.7)#41

Open
canonical-muhammadbassiony wants to merge 4 commits into
feat/figma-phase-b-clientfrom
feat/figma-phase-c-mapping
Open

feat(figma): design-to-text mapping resolver (Phase C — T2F.5, T2F.6, T2F.7)#41
canonical-muhammadbassiony wants to merge 4 commits into
feat/figma-phase-b-clientfrom
feat/figma-phase-c-mapping

Conversation

@canonical-muhammadbassiony

Copy link
Copy Markdown
Collaborator

Summary

Introduces the mapping resolver that joins Google Docs suggestion groups with Figma design data, and integrates the result into prompts.

Tasks Implemented

  • T2F.5: internal/source/mapping — resolver with 4-strategy priority chain: (1) user-supplied node ID (confidence 1.0), (2) Jaccard text-layer similarity (threshold 0.30), (3) frame-name overlap (threshold 0.50), (4) fallback to first anchor (status "unresolved")
  • T2F.6: Prompt engine gains FigmaContextJSON and FigmaURL fields in PromptData; GenerateChunksFromResolved batches resolved chunks into prompt data; RenderChunk conditionally renders figma-context template
  • T2F.7: Artifact persistence — WriteFigmaExtraction, WriteMappings, WriteFigmaComments methods; figma-context.md template with anchors, screenshots, and comments sections

Files Changed

  • internal/source/mapping/types.goResolvedChunk, DesignAnchorRef, DesignCommentRef, MappingMetadata
  • internal/source/mapping/resolver.goResolver.Build with Jaccard matching
  • internal/source/mapping/resolver_test.go — 9 test cases
  • internal/prompt/engine.goGenerateChunksFromResolved, buildFigmaContextJSON, figma template rendering
  • internal/prompt/templates/figma-context.md — design context template
  • internal/prompt/engine_test.go — 6 new test functions
  • internal/artifacts/manager.goWriteFigmaExtraction, WriteMappings, WriteFigmaComments
  • internal/artifacts/manager_test.go — tests for new methods

Part of the Bauer v2 stacked PR series (Branch 6 of 12).

Bauer Agent added 2 commits May 20, 2026 13:19
…persistence

- T2F.5: internal/source/mapping resolver with Jaccard text matching + fallback
- T2F.6: prompt engine GenerateChunksFromResolved, FigmaContextJSON, figma-context template
- T2F.7: artifacts.WriteFigmaExtraction, WriteMappings, WriteFigmaComments

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new design-to-text mapping layer that enriches Google Docs suggestion groups with extracted Figma design context, then threads that enrichment into prompt rendering and artifact persistence for later inspection/debugging.

Changes:

  • Introduces internal/source/mapping with ResolvedChunk types and a 4-strategy resolver (URL node ID → text similarity → frame-name overlap → fallback).
  • Extends the prompt engine to batch ResolvedChunks into PromptData, serialize design enrichment into FigmaContextJSON, and conditionally render a new figma-context.md section.
  • Adds artifact manager helpers to persist normalized Figma extraction, resolved mappings, and extracted comments under extraction/.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/source/mapping/types.go Adds resolved-chunk and mapping metadata types for Figma enrichment.
internal/source/mapping/resolver.go Implements resolver strategies (URL/text/name/fallback) and attaches screenshots/comments.
internal/source/mapping/resolver_test.go Adds tests for resolver behavior across matching strategies and attachments.
internal/prompt/engine.go Adds Figma context fields, batching from resolved chunks, and conditional figma-context rendering.
internal/prompt/templates/figma-context.md Adds a prompt section template for anchors/screenshots/designer comments.
internal/prompt/engine_test.go Adds tests for batching and figma-context inclusion/exclusion during rendering.
internal/artifacts/manager.go Adds persistence methods for Figma extraction, mappings, and comments.
internal/artifacts/manager_test.go Adds tests verifying the new artifact outputs are written and parseable.
docs/implementation-log.md Marks Phase C as done and documents the delivered changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/source/mapping/resolver.go Outdated
Comment on lines +85 to +86
// matchByTextLayers uses weighted Jaccard similarity on token bags.
// Returns the best matching anchor and its confidence, or nil if no match meets threshold.
Comment thread internal/source/mapping/resolver.go Outdated
Comment on lines +73 to +80
// Strategy 4: fallback to first anchor (root node)
if len(design.Anchors) > 0 {
return []DesignAnchorRef{{
FileKey: design.FileKey,
NodeID: design.Anchors[0].NodeID,
NodeName: design.Anchors[0].NodeName,
}}, MappingMetadata{Method: "fallback", Confidence: 0.50, Status: "unresolved"}
}
Comment thread internal/source/mapping/resolver.go Outdated
Comment on lines +115 to +118
if jacc >= 0.30 && conf > bestConf {
bestConf = conf
best = &anchors[i]
}
Comment thread internal/source/mapping/resolver.go Outdated
Comment on lines +148 to +151
if overlap >= 0.50 && conf > bestConf {
bestConf = conf
best = &anchors[i]
}
Comment thread internal/prompt/engine.go
Comment on lines +144 to +158
// Append figma context section when design data is present
if data.FigmaContextJSON != "" {
var ctx figmaChunkContext
if err := json.Unmarshal([]byte(data.FigmaContextJSON), &ctx); err != nil {
return "", fmt.Errorf("parsing figma context JSON: %w", err)
}
tmpl, err := template.New("figma-context").Parse(figmaContextTemplate)
if err != nil {
return "", fmt.Errorf("parsing figma context template: %w", err)
}
buf.WriteString("\n\n---\n\n")
if err := tmpl.Execute(&buf, ctx); err != nil {
return "", fmt.Errorf("rendering figma context: %w", err)
}
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Comment thread internal/source/mapping/resolver.go Outdated
}}, MappingMetadata{Method: "name", Confidence: conf, Status: "healthy"}
}

// Strategy 4: fallback to first anchor (root node), sorted for determinism
Comment thread internal/source/mapping/resolver.go Outdated
Comment on lines +91 to +95
// matchByTextLayers uses Jaccard similarity on token bags.
// Returns the best matching anchor and its confidence, or nil if no match meets threshold.
func matchByTextLayers(group gdocs.LocationGroupedSuggestions, anchors []figma.DesignAnchor) (*figma.DesignAnchor, float64) {
// Build token bag from gdocs suggestion group
gdocsTokens := tokenize(group.Location.ParentHeading + " " + group.Location.Section)
Comment thread internal/prompt/engine.go Outdated
Comment on lines +256 to +259
suggestionsJSON, err := json.Marshal(locations)
if err != nil {
return nil, fmt.Errorf("marshaling suggestions for chunk %d: %w", i+1, err)
}
Comment thread internal/prompt/engine.go
Comment on lines +280 to +297
func buildFigmaContextJSON(batch []mapping.ResolvedChunk) (string, error) {
ctx := figmaChunkContext{}
for _, rc := range batch {
ctx.Anchors = append(ctx.Anchors, rc.DesignAnchors...)
ctx.Screenshots = append(ctx.Screenshots, rc.ScreenshotPaths...)
ctx.Comments = append(ctx.Comments, rc.Comments...)
}

if len(ctx.Anchors) == 0 && len(ctx.Screenshots) == 0 && len(ctx.Comments) == 0 {
return "", nil
}

b, err := json.Marshal(ctx)
if err != nil {
return "", err
}
return string(b), nil
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment on lines +201 to +218
// tokenize normalizes text into a lowercase token slice, removing stop words and short tokens.
func tokenize(text string) []string {
stop := map[string]bool{
"the": true, "a": true, "an": true, "and": true, "or": true,
"in": true, "of": true, "to": true, "for": true, "is": true, "are": true,
"it": true, "at": true, "on": true, "by": true, "be": true,
}
words := strings.FieldsFunc(strings.ToLower(text), func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsDigit(r)
})
var result []string
for _, w := range words {
if len(w) >= 3 && !stop[w] {
result = append(result, w)
}
}
return result
}
@@ -0,0 +1,32 @@
## Design Context

Design information has been extracted from Figma for the suggestions in this chunk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants