fix: Map expansion now matches nested child steps (gt-8tmz.33)
The map rule was only iterating over top-level steps, missing nested children. Now uses buildStepMap to include all steps at any depth. Added test case for map expansion over nested children. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,14 @@
|
||||
{"id":"bd-14ie","title":"Work on beads-2vn: Add simple built-in beads viewer (GH#6...","description":"Work on beads-2vn: Add simple built-in beads viewer (GH#654). Add bd list --pretty with --watch flag, tree view with priority/status symbols. When done, submit MR (not PR) to integration branch for Refinery.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:56:47.305831-08:00","updated_at":"2025-12-19T23:28:32.429492-08:00","closed_at":"2025-12-19T23:23:13.928323-08:00","close_reason":"Implemented --pretty flag with tree view and symbols. Tests pass."}
|
||||
{"id":"bd-14v0","title":"Add Windows code signing for bd.exe releases","description":"## Context\n\nGo binaries (including bd.exe) are commonly flagged by antivirus software as false positives due to heuristic detection. See docs/ANTIVIRUS.md for full details.\n\n## Problem\n\nKaspersky and other AV software flag bd.exe as PDM:Trojan.Win32.Generic, causing it to be quarantined or deleted.\n\n## Solution\n\nImplement code signing for Windows releases using:\n1. An EV (Extended Validation) code certificate\n2. Integration with GoReleaser to sign Windows binaries during release\n\n## Benefits\n\n- Reduces false positive rates over time as the certificate builds reputation\n- Provides tamper verification for users\n- Improves SmartScreen trust rating on Windows\n- Professional appearance for enterprise users\n\n## Implementation Steps\n\n1. Acquire EV code signing certificate (annual cost ~$300-500)\n2. Set up signtool or osslsigncode in release pipeline\n3. Update .goreleaser.yml to sign Windows binaries\n4. Update checksums to include signed binary hashes\n5. Document signing verification in ANTIVIRUS.md\n\n## References\n\n- docs/ANTIVIRUS.md - Current documentation\n- bd-t4u1 - Original Kaspersky false positive report\n- https://github.com/golang/go/issues/16292 - Go project discussion","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-23T23:46:48.459177-08:00","updated_at":"2025-12-23T23:54:41.912141-08:00","closed_at":"2025-12-23T23:54:41.912141-08:00","close_reason":"Implemented Windows code signing infrastructure. Added signing script, GoReleaser hook, updated release workflow and documentation. Signing is gracefully degraded when certificate secrets are not configured - releases continue as unsigned. Certificate acquisition (EV cert) is still required to actually enable signing.","dependencies":[{"issue_id":"bd-14v0","depends_on_id":"bd-t4u1","type":"discovered-from","created_at":"2025-12-23T23:47:02.024159-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez","title":"Mol Mall: Formula marketplace using GitHub as backend","description":"Create a marketplace for sharing molecule formulas using GitHub repos as the hosting backend.\n\n## Why GitHub?\n\nGitHub solves multiple problems at once:\n- **Hosting**: Raw file URLs for formula.json\n- **Versioning**: Git tags (v1.0.0, v1.2.0)\n- **Auth**: GitHub tokens for private formulas\n- **Discovery**: GitHub search, topics, stars\n- **Collaboration**: PRs for contributions, issues for bugs\n- **Organizations**: Natural scoping (@anthropic/, @gastown/)\n\n## URL Scheme\n\n```bash\n# Direct GitHub URL\nbd mol install github.com/anthropics/mol-code-review\n\n# With version tag\nbd mol install github.com/anthropics/mol-code-review@v1.2.0\n\n# Shorthand (via registry lookup)\nbd mol install @anthropic/mol-code-review\n```\n\n## Architecture\n\nEach formula lives in its own repo (like Go modules):\n```\ngithub.com/anthropics/mol-code-review/\n├── formula.json # The formula\n├── README.md # Documentation\n└── CHANGELOG.md # Version history\n```\n\n## ID Namespace\n\n| Entity | ID Format | Example |\n|--------|-----------|---------|\n| Formula (GitHub) | `github.com/org/repo` | `github.com/anthropics/mol-code-review` |\n| Catalog proto (local) | `mol-name` | `mol-code-review` |\n| Distilled proto | `\u003cdb\u003e-proto-xxx` | `bd-proto-a7f` |\n| Poured instance | `\u003cdb\u003e-mol-xxx` | `gt-mol-b8c` |\n","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-25T12:05:17.666574-08:00","updated_at":"2025-12-25T12:05:17.666574-08:00"}
|
||||
{"id":"bd-1dez.1","title":"bd mol export: Convert proto to formula file","description":"Export a proto (template issue subgraph) to a formula.json file.\n\n## Usage\n```bash\nbd mol export bd-proto-xyz --output mol-my-workflow.formula.json\nbd mol export mol-polecat-work \u003e polecat-work.formula.json\n```\n\n## Implementation\n1. Load proto subgraph (root + all children)\n2. Convert to formula JSON structure\n3. Extract variables from {{var}} patterns\n4. Generate step IDs from issue titles (slugify)\n5. Write to file\n\n## Output Format\n```json\n{\n \"formula\": \"mol-my-workflow\",\n \"description\": \"...\",\n \"version\": 1,\n \"variables\": [...],\n \"steps\": [...]\n}\n```\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:05:47.045105-08:00","updated_at":"2025-12-25T12:05:47.045105-08:00","dependencies":[{"issue_id":"bd-1dez.1","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:05:47.045596-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.2","title":"bd mol promote: Convert distilled proto to catalog proto","description":"One-step conversion from distilled proto (bd-proto-xxx) to catalog proto (mol-xxx).\n\n## Usage\n```bash\nbd mol promote bd-proto-xyz --as my-workflow\n# Creates: .beads/formulas/mol-my-workflow.formula.json\n# Cooks it to create: mol-my-workflow proto\n# Optionally deletes: bd-proto-xyz\n```\n\n## Implementation\n1. Run export internally (proto → formula)\n2. Set formula name from --as flag\n3. Save to .beads/formulas/\n4. Run cook internally (formula → catalog proto)\n5. Optionally delete original distilled proto (--keep to preserve)\n\n## Flags\n- `--as \u003cname\u003e` - Name for the catalog proto (required)\n- `--keep` - Keep the original distilled proto\n- `--output \u003cdir\u003e` - Where to save formula (default: .beads/formulas/)\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:05:48.588283-08:00","updated_at":"2025-12-25T12:05:48.588283-08:00","dependencies":[{"issue_id":"bd-1dez.2","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:05:48.590203-08:00","created_by":"daemon"},{"issue_id":"bd-1dez.2","depends_on_id":"bd-1dez.1","type":"blocks","created_at":"2025-12-25T12:07:06.745686-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.3","title":"bd mol install: Install formula from GitHub","description":"Download and cook a formula from a GitHub repository.\n\n## Usage\n```bash\n# Direct GitHub URL\nbd mol install github.com/anthropics/mol-code-review\n\n# With version tag \nbd mol install github.com/anthropics/mol-code-review@v1.2.0\n\n# Shorthand (future: via registry lookup)\nbd mol install @anthropic/mol-code-review\n```\n\n## Implementation\n1. Parse URL: extract org, repo, optional version tag\n2. Construct raw URL: `https://raw.githubusercontent.com/org/repo/[tag]/formula.json`\n3. Fetch formula.json via HTTP\n4. Validate formula structure\n5. Save to .beads/formulas/\n6. Run cook to create local proto\n7. Record in .beads/installed.json for update tracking\n\n## installed.json Format\n```json\n{\n \"mol-code-review\": {\n \"source\": \"github.com/anthropics/mol-code-review\",\n \"version\": \"v1.2.0\",\n \"installed_at\": \"2025-12-25T12:00:00Z\"\n }\n}\n```\n\n## Dependencies\n- Requires network access\n- Uses `gh` CLI or direct HTTP for auth (private repos)\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:05:49.757336-08:00","updated_at":"2025-12-25T12:05:49.757336-08:00","dependencies":[{"issue_id":"bd-1dez.3","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:05:49.759176-08:00","created_by":"daemon"},{"issue_id":"bd-1dez.3","depends_on_id":"bd-1dez.7","type":"blocks","created_at":"2025-12-25T12:07:06.825716-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.4","title":"bd mol update: Check and update installed formulas","description":"Check for newer versions of installed formulas and update them.\n\n## Usage\n```bash\nbd mol update # Update all\nbd mol update mol-code-review # Update specific formula\nbd mol update --check # Just check, don't update\n```\n\n## Implementation\n1. Read .beads/installed.json\n2. For each installed formula:\n - Fetch latest tag from GitHub API\n - Compare with installed version\n - If newer: download, cook, update installed.json\n3. Report what was updated\n\n## Flags\n- `--check` - Only check for updates, don't install\n- `--force` - Reinstall even if up to date\n- `--json` - Machine-readable output\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:05:50.952041-08:00","updated_at":"2025-12-25T12:05:50.952041-08:00","dependencies":[{"issue_id":"bd-1dez.4","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:05:50.952584-08:00","created_by":"daemon"},{"issue_id":"bd-1dez.4","depends_on_id":"bd-1dez.3","type":"blocks","created_at":"2025-12-25T12:07:06.90486-08:00","created_by":"daemon"},{"issue_id":"bd-1dez.4","depends_on_id":"bd-1dez.8","type":"blocks","created_at":"2025-12-25T12:07:06.99578-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.5","title":"bd mol search: Find formulas using GitHub API","description":"Search for formulas in the Mol Mall using GitHub's search API.\n\n## Usage\n```bash\nbd mol search \"code review\" # Free text search\nbd mol search --topic molecule # By GitHub topic\nbd mol search --org anthropics # By organization\nbd mol search --stars \"\u003e10\" # By popularity\n```\n\n## Implementation\n1. Use GitHub Search API: `/search/repositories`\n2. Filter by topic:mol-formula or naming convention\n3. Parse results, show name/description/stars/version\n4. Support pagination for large result sets\n\n## Output\n```\nNAME STARS DESCRIPTION\ngithub.com/anthropics/mol-code-review 42 AI-assisted code review workflow\ngithub.com/gastown/mol-polecat-work 12 Standard polecat work lifecycle\n```\n\n## Discovery Convention\nRepos should have:\n- Topic: `mol-formula` or `beads-molecule`\n- Name starting with `mol-`\n- formula.json in root\n","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-25T12:06:40.019394-08:00","updated_at":"2025-12-25T12:06:40.019394-08:00","dependencies":[{"issue_id":"bd-1dez.5","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:06:40.01989-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.6","title":"bd mol publish: Push formula to GitHub repo","description":"Publish a formula to a GitHub repository for sharing.\n\n## Usage\n```bash\n# Create new repo and publish\nbd mol publish mol-my-workflow --repo github.com/myorg/mol-my-workflow\n\n# Update existing repo\nbd mol publish mol-my-workflow --repo github.com/myorg/mol-my-workflow --tag v1.1.0\n```\n\n## Implementation\n1. Export proto to formula.json (if not already a file)\n2. Validate formula structure\n3. Create GitHub repo if --create flag (uses `gh repo create`)\n4. Copy formula.json to repo\n5. Generate README.md from formula description\n6. Commit and push\n7. Create git tag if --tag specified\n8. Add mol-formula topic to repo\n\n## Flags\n- `--repo \u003curl\u003e` - Target GitHub repo (required)\n- `--tag \u003cversion\u003e` - Create version tag\n- `--create` - Create repo if doesn't exist\n- `--private` - Create as private repo\n\n## Workflow\n```bash\n# Full publish flow\nbd mol distill my-epic --name my-workflow\nbd mol promote bd-proto-xxx --as my-workflow\nbd mol publish mol-my-workflow --repo github.com/me/mol-my-workflow --create --tag v1.0.0\n```\n","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-25T12:06:41.419764-08:00","updated_at":"2025-12-25T12:06:41.419764-08:00","dependencies":[{"issue_id":"bd-1dez.6","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:06:41.420209-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.7","title":"Formula versioning: Version field and git tag integration","description":"Add versioning support to formulas for tracking updates.\n\n## Formula Version Field\n```json\n{\n \"formula\": \"mol-code-review\",\n \"version\": \"1.2.0\",\n \"min_bd_version\": \"0.25.0\",\n ...\n}\n```\n\n## Version Semantics\n- Use semver: MAJOR.MINOR.PATCH\n- MAJOR: Breaking changes to step structure\n- MINOR: New optional steps or variables\n- PATCH: Documentation, bug fixes\n\n## Git Tag Integration\n- `bd mol install repo@v1.2.0` fetches that tag\n- `bd mol install repo` fetches latest tag (or main if no tags)\n- `bd mol publish --tag v1.2.0` creates tag\n\n## Proto Version Tracking\nLocal proto should store:\n```\nsource_repo: github.com/anthropics/mol-code-review\nsource_version: v1.2.0\ninstalled_at: 2025-12-25T12:00:00Z\n```\n\nThis enables `bd mol update` to check for newer versions.\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:06:42.674849-08:00","updated_at":"2025-12-25T12:06:42.674849-08:00","dependencies":[{"issue_id":"bd-1dez.7","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:06:42.67694-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1dez.8","title":"Installation tracking: .beads/installed.json","description":"Track installed formulas for update checking and provenance.\n\n## File Format\n```json\n{\n \"mol-code-review\": {\n \"source\": \"github.com/anthropics/mol-code-review\",\n \"version\": \"v1.2.0\",\n \"installed_at\": \"2025-12-25T12:00:00Z\",\n \"formula_hash\": \"abc123...\"\n },\n \"mol-polecat-work\": {\n \"source\": \"local\",\n \"version\": \"1\",\n \"installed_at\": \"2025-12-24T10:00:00Z\"\n }\n}\n```\n\n## Commands That Update This\n- `bd mol install` - Adds entry\n- `bd mol update` - Updates version/timestamp\n- `bd cook` - Adds entry with source:local\n- `bd mol uninstall` - Removes entry (new command)\n\n## Usage\n```bash\nbd mol list --installed # Shows installed with source/version\nbd mol outdated # Shows formulas with available updates\n```\n\n## Sync Behavior\n- installed.json is gitignored (local state)\n- Only formulas/ directory is synced\n- Each clone tracks its own installations\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-25T12:06:43.934727-08:00","updated_at":"2025-12-25T12:06:43.934727-08:00","dependencies":[{"issue_id":"bd-1dez.8","depends_on_id":"bd-1dez","type":"parent-child","created_at":"2025-12-25T12:06:43.937653-08:00","created_by":"daemon"}]}
|
||||
{"id":"bd-1slh","title":"Investigate charmbracelet-based TUI for beads","description":"Now that we've merged the create-form command (PR #603) which uses charmbracelet/huh, investigate whether beads should have a more comprehensive TUI.\n\nConsiderations:\n- Should this be in core or a separate binary (bd-tui)?\n- What functionality would benefit from a TUI? (list view, issue details, search, bulk operations)\n- Plugin/extension architecture vs build tags vs separate binary\n- Dependency cost vs user experience tradeoff\n- Target audience: humans who want interactive workflows vs CLI/scripting users\n\nRelated: PR #603 added charmbracelet/huh dependency for create-form command.","notes":"Foundation is in place (lipgloss, huh), but not a priority right now","status":"deferred","priority":3,"issue_type":"feature","created_at":"2025-12-17T14:20:51.503563-08:00","updated_at":"2025-12-20T23:31:34.354023-08:00"}
|
||||
{"id":"bd-1tw","title":"Fix G104 errors unhandled in internal/storage/sqlite/queries.go:1186","description":"Linting issue: G104: Errors unhandled (gosec) at internal/storage/sqlite/queries.go:1186:2. Error: rows.Close()","status":"tombstone","priority":0,"issue_type":"bug","created_at":"2025-12-07T15:35:13.051671889-07:00","updated_at":"2025-12-25T01:21:01.952723-08:00","deleted_at":"2025-12-25T01:21:01.952723-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"}
|
||||
{"id":"bd-20j","title":"sync branch not match config","description":"./bd sync\n→ Exporting pending changes to JSONL...\n→ No changes to commit\n→ Pulling from sync branch 'gh-386'...\nError pulling from sync branch: failed to create worktree: failed to create worktree parent directory: mkdir /var/home/matt/dev/beads/worktree-db-fail/.git: not a directory\nmatt@blufin-framation ~/d/b/worktree-db-fail (worktree-db-fail) [1]\u003e bd config list\n\nConfiguration:\n auto_compact_enabled = false\n compact_batch_size = 50\n compact_model = claude-3-5-haiku-20241022\n compact_parallel_workers = 5\n compact_tier1_days = 30\n compact_tier1_dep_levels = 2\n compact_tier2_commits = 100\n compact_tier2_days = 90\n compact_tier2_dep_levels = 5\n compaction_enabled = false\n issue_prefix = worktree-db-fail\n sync.branch = worktree-db-fail","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-08T06:49:04.449094018-07:00","updated_at":"2025-12-08T06:49:04.449094018-07:00"}
|
||||
|
||||
@@ -112,10 +112,12 @@ func ApplyExpansions(steps []*Step, compose *ComposeRules, parser *Parser) ([]*S
|
||||
return nil, fmt.Errorf("map: %q has no template steps", rule.With)
|
||||
}
|
||||
|
||||
// Find all matching steps
|
||||
// Find all matching steps (including nested children - gt-8tmz.33)
|
||||
// Rebuild stepMap to capture any changes from previous expansions
|
||||
stepMap = buildStepMap(result)
|
||||
var toExpand []*Step
|
||||
for _, step := range result {
|
||||
if MatchGlob(rule.Select, step.ID) && !expanded[step.ID] {
|
||||
for id, step := range stepMap {
|
||||
if MatchGlob(rule.Select, id) && !expanded[id] {
|
||||
toExpand = append(toExpand, step)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +287,59 @@ func TestApplyExpansions(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// Test map over nested children (gt-8tmz.33)
|
||||
t.Run("map over nested children", func(t *testing.T) {
|
||||
steps := []*Step{
|
||||
{ID: "design", Title: "Design"},
|
||||
{
|
||||
ID: "phase",
|
||||
Title: "Implementation Phase",
|
||||
Children: []*Step{
|
||||
{ID: "implement.auth", Title: "Implement auth"},
|
||||
{ID: "implement.api", Title: "Implement API"},
|
||||
},
|
||||
},
|
||||
{ID: "test", Title: "Test"},
|
||||
}
|
||||
|
||||
compose := &ComposeRules{
|
||||
Map: []*MapRule{
|
||||
{Select: "*.auth", With: "rule-of-five"},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := ApplyExpansions(steps, compose, parser)
|
||||
if err != nil {
|
||||
t.Fatalf("ApplyExpansions failed: %v", err)
|
||||
}
|
||||
|
||||
// The nested implement.auth should be expanded
|
||||
// Result should have: design, phase (with expanded children), test
|
||||
if len(result) != 3 {
|
||||
t.Fatalf("expected 3 top-level steps, got %d", len(result))
|
||||
}
|
||||
|
||||
// Check that phase has expanded children
|
||||
phase := result[1]
|
||||
if phase.ID != "phase" {
|
||||
t.Fatalf("expected phase step, got %q", phase.ID)
|
||||
}
|
||||
|
||||
// implement.auth expanded to 2 steps + implement.api unchanged = 3 children
|
||||
if len(phase.Children) != 3 {
|
||||
t.Fatalf("expected 3 children in phase, got %d: %v", len(phase.Children), getChildIDs(phase.Children))
|
||||
}
|
||||
|
||||
// Verify expanded IDs
|
||||
childIDs := getChildIDs(phase.Children)
|
||||
expectedChildren := []string{"implement.auth.draft", "implement.auth.refine", "implement.api"}
|
||||
for i, exp := range expectedChildren {
|
||||
if childIDs[i] != exp {
|
||||
t.Errorf("phase.Children[%d].ID = %q, want %q", i, childIDs[i], exp)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Test missing formula
|
||||
t.Run("missing expansion formula", func(t *testing.T) {
|
||||
steps := []*Step{{ID: "test", Title: "Test"}}
|
||||
@@ -370,3 +423,12 @@ func TestUpdateDependenciesForExpansion(t *testing.T) {
|
||||
t.Errorf("deploy step DependsOn[1] = %q, want %q", result[2].DependsOn[1], "test")
|
||||
}
|
||||
}
|
||||
|
||||
// getChildIDs extracts IDs from a slice of steps (helper for tests).
|
||||
func getChildIDs(steps []*Step) []string {
|
||||
ids := make([]string, len(steps))
|
||||
for i, s := range steps {
|
||||
ids[i] = s.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user