Skip to content

CLI: scan collapses Figma's COMPLETED dev status to NONE, and the manifest parser silently drops rows with unknown status values #362

Description

@devin-ai-integration

Figma's Dev Status has three values a designer can set: READY_FOR_DEV, COMPLETED, and none. The CLI recognizes only READY_FOR_DEV; COMPLETED — set when the design work is finished, arguably a stronger curation signal than "ready for dev" — is silently collapsed to NONE and becomes indistinguishable from a component nobody has touched. A second, closed vocabulary in the manifest parser then makes any attempt to record the real status silently lossy.

Package: @directededges/specs-cli — found on 0.26.0, still present on 0.27.0 (current release; verified in the shipped bundle 2026-08-29).

The string COMPLETED does not appear anywhere in dist/.

1. scan discards every dev status except READY_FOR_DEV

FigmaFileDiscovery.findAllComponents() (dist/specs.js, two occurrences):

devStatus: node.devStatus?.type === "READY_FOR_DEV" ? "READY_FOR_DEV" : "NONE"

In our library file that is 39 COMPLETED components against 4 READY_FOR_DEV, so a manifest-driven generate selected 4 of 43. Note specs fetch is fine — the payload it writes carries COMPLETED correctly; only the scan-side mapping drops it.

2. The manifest parser's closed vocabulary silently drops rows

var ROW_REGEX = /^\|\s*\[([x ])\]\s*\|\s*(.+?)\s*\|\s*(\d+:\d+)\s*\|\s*(COMPONENT_SET|COMPONENT)\s*\|\s*(READY_FOR_DEV|NONE)\s*\|/i;

ROW_REGEX accepts only (READY_FOR_DEV|NONE) in the status column, and generate in manifest mode (ManifestParserV2.parse.filter(c => c.included)) runs every row through it. A row it cannot parse is dropped, not merely unselected — and the ✓ Loaded manifest: N components (M selected) line counts only survivors, so the run looks healthy.

That makes the manifest silently lossy for any status value the CLI does not itself emit, and it is what turns (1) from "a mapping gap" into a metered spending decision applied to the wrong set with no error.

Reproduction, verified against the shipped regex:

| [x] | Text Field | 9313:18494 | COMPONENT_SET | COMPLETED |          DROPPED
| [x] | Text Field | 9313:18494 | COMPONENT_SET | NONE | COMPLETED |   PARSED (extra cell ignored)

Suggested fix

  • Carry Figma's devStatus.type through verbatim instead of mapping to a closed two-value set.
  • In the manifest parser, treat unknown status values as unselected rather than unparseable.
  • Warn when a manifest row fails to parse instead of dropping it silently.

Our current workaround

We wrap specs scan in a script that re-reads the fetched payload and appends a sixth column carrying the real Figma status, leaving column 5 in the CLI's own vocabulary so ROW_REGEX still parses the row (it is not anchored to end-of-line). All of that machinery can be deleted the day the CLI carries the status through itself.

Two smaller behaviours, observed on 0.26.0 (not re-checked on 0.27.0)

  • Without --split-components, a per-component generate loop writes one shared library.yaml and overwrites it wholesale per invocation — billing a metered generation each and keeping only the last.
  • Generating a renamed component writes a new derived filename without removing the old spec for the same nodeId; on a case-insensitive filesystem the two can differ only in case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      Ready to release

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions