groups-compose — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited groups-compose (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 4 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 4 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Skip if fewer than 3 members or the tie is just generation order.
canvas_layout.js to write member positions and groupFrames in one atomic sidecar update.workflow.json; never put x / y into node data.x, y, width, height) is a bounding box computed from the final member positions.frameId: frame_<unix_ms>. Titles ≤30 chars recommended.Pick the one that fits. Grouping is current canvas state; read workflow.json per the project PROJECT_AGENT.md § "Choosing context" to verify ids.
Scene <N> — <location or beat>.A character card + its reference images.
<Character name> — references.image_result nodes depicting the same character.Act <N> or beat name.x / y AND the existing groupFrames map. Reads are unrestricted; writes go through canvas_layout.js.nodes.groupFrames.upsert with memberIds minus the evictee.groupFrames.delete.note: 280 × 420 (width hardcoded; height = NOTE_CARD_FALLBACK_HEIGHT for first paint)image_result: 290 × 220 (16:9 default; if data.metadata.aspect_ratio is present, scale accordingly)video_result: 290 × 220 (same caveat; check data.aspect or data.metadata.aspect_ratio)audio_result: 240 × 64pending / pending_generation / pending_attachment: 260 × 200If measured heights appear in canvas_positions.json, prefer them.
minX = min(node.x for each member)
minY = min(node.y for each member)
maxX = max(node.x + node.w for each member)
maxY = max(node.y + node.h for each member)
x = minX - 24
y = minY - 24
width = (maxX - minX) + 48
height = (maxY - minY) + 48 node "$PAI_REPO_ROOT/server/cli/canvas_layout.js" \
--layout-json '{"positions":{"note_2":{"x":120,"y":80},"image_3":{"x":440,"y":80},"video_1":{"x":760,"y":80}},"groupFrames":{"upsert":{"frame_1716579123456":{"memberIds":["note_2","image_3","video_1"],"x":96,"y":56,"width":978,"height":468,"hue":200,"title":"Scene 1 — Causeway"}},"delete":[]}}'memberIds, recomputed bbox.canvas_positions.json directly. Use canvas_layout.js so positions and frames apply together.addGroup, updateGroup, deleteGroup).memberIds).~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.