Validated against Granite workflow source (com.adobe.granite.workflow.api,
com.adobe.granite.workflow.api.it.http) and a live AEM instance.
HTTP Workflow API:
- Root the API at /var/workflow/instances; warn that POST to
/api/workflow/instances writes a stray JCR node and returns a misleading
2xx without starting a workflow (clean GET there returns 404)
- Document start (201 + <a id=Location>), JSON detail fields, state-selector
listing, and suspend/resume/terminate via state= POSTs
Programmatic API:
- Use WorkflowSession.terminateWorkflow() (terminate() does not exist)
- Use Workflow.State.RUNNING.name() (Workflow.STATUS_RUNNING does not exist)
Service user:
- Map a subservice to a service user that is a member of the
workflow-process-service group (a group, not a user); add repoinit example
Manage Publication:
- Note the payload is a JCR_PATH to the package node under
/var/workflow/packages (not the individual pages) for payload debugging
Co-authored-by: akanjain <akanjain@adobe.com>
The triggering skill documented a non-existent HTTP API: POST/GET
/api/workflow/instances to start and list, and DELETE /api/workflow/instances/<id>
to terminate. None of these are real Granite Workflow endpoints. POSTing to
/api/workflow/instances is swallowed by the Sling default POST servlet, which
silently writes stray JCR nodes and returns a 2xx without starting any workflow,
so the trigger appears to succeed but no instance is ever created.
Replace with the actual contract, verified end-to-end against a running AEM 6.5
LTS instance (start, list, detail, terminate, and the /api 404 guard all pass):
- Start: POST /var/workflow/instances (legacy /etc/workflow/instances also
accepted), form fields _charset_, model, payloadType, payload,
workflowTitle, startComment -> 201, HTML body linking the instance path
- List: GET /var/workflow/instances.json, filter via selector .RUNNING.json
- Detail: GET <instanceId>.json
- Terminate: POST <instanceId> with state=ABORTED -> 200
Also document the start response being HTML (not JSON) and add a warning about the
/api/workflow/instances silent-success trap. Apply the same quick-start-guide.md fix
across the model-design, orchestrator, triggering, and development copies to keep the
shared foundation reference consistent.
Co-authored-by: akanjain <akanjain@adobe.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(aem-workflow): mark workflow-development skill as beta
Apply beta markers (status: beta frontmatter, [BETA] description prefix with caveat, body blockquote) to workflow-development in both 6.5-lts and cloud-service trees.
* fix(aem-workflow): nest beta status under metadata to pass schema validation
Top-level 'status' is rejected by skills-ref validate; nest it under metadata, matching the aem-rde beta skill precedent.
---------
Co-authored-by: akankshajain18 <akankshajain18@gmail.com>
Same pattern as the prior 5 extractions. aa-executive-briefing slipped to
77% on the most recent tessl-review re-run (it was passing previously but
hovered near the threshold). Move the ~316-line inline HTML/CSS report
template into a sibling template.html and reference it from SKILL.md.
Local tessl review now scores 81%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trim three sections per tessl-review judge feedback: drop the "Why This
Matters" intro, condense the Gini and z-score explanations (concepts Claude
already knows), and remove the inline JSON schema block (the consuming
Python script defines its own contract and the per-phase Store shapes
already document the data). Local tessl review now scores 90% (was 79%).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move large inline HTML/CSS report templates from each SKILL.md into a
sibling template.html. SKILL.md now references the template with a one-line
instruction to read and use it verbatim, filling in only the named
placeholders. Addresses tessl-review feedback on conciseness and
progressive disclosure for: aa-kpi-pulse, aa-segment-performance-comparator,
cja-dimension-analysis, cja-kpi-pulse, cja-segment-performance-comparator.
No behavioral changes — the generated reports remain identical.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two new MCP-backed plugins for analytics practitioners:
- adobe-analytics — 5 skills targeting the Adobe Analytics MCP server
(KPI pulse, top movers, conversion funnel analysis, segment performance
comparator, executive briefing).
- adobe-cja — 6 skills targeting the Customer Journey Analytics MCP server
(KPI pulse, top movers, funnel health check, dimension analysis, segment
performance comparator, executive briefing).
Each plugin ships a .mcp.json pointing at its hosted MCP endpoint
(https://aa-mcp.adobe.io/mcp and https://cja-mcp.adobe.io/mcp) so the
skills work out of the box once the user has product access.
Repo-wide updates:
- Register both plugins in .claude-plugin/marketplace.json.
- Add a new "Analytics" group to the top-level README under For Business,
with per-skill descriptions, sample prompts, and links to each plugin
README, plus install commands in the Installation section.
- Add CODEOWNERS entries for the two new plugin directories.
- Every new SKILL.md declares license: Apache-2.0 in its frontmatter.
Co-authored-by: Cursor <cursoragent@cursor.com>
Deliberately high for now to avoid premature auto-switching while we
gather data on real pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Block-level conversion generates N independent blocks — own JS, CSS,
content model, no shared state. Phase 3 (B.5) now explicitly states
blocks can be generated in parallel if the host supports concurrent
work dispatch. The hint is intent-level (no specific tool prescribed),
matching the browser-intent pattern.
HOST-NOTES updated: replaced the "out of scope in v1" parallelism
disclaimers with per-host guidance — Slicc dispatches one scoop per
block via the cone, Claude Code dispatches one Agent subagent per block.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an <a> contains authorable text alongside decorative non-authorable
children (inline SVGs, icon images), placing data-slot on the <a> causes
writeSlot to destroy the decorative content at runtime.
Adds:
- Learnings entry documenting the pattern and the general rule
- Phase 2 mixed-content detection: slots flagged with mixedContent=true
in decisions.json when <a> contains SVG/decorative-img/icon children
- Phase 3 span-wrapper rule: wrap only the authorable text in a
<span data-slot>, leaving decorative siblings as template chrome
- Self-check #7: DOM-based post-generate validation that no data-slot
element contains SVG or decorative image descendants
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Large pages with many sections or slottable elements risk silent content
loss during page-level conversion (context pressure, repetitive-structure
fatigue). Block-level processes one section at a time and is immune.
Phase 2 now checks: >8 sections or >100 slottable elements. When the gate
fires and level=page was the default (not explicitly requested), it
auto-switches to block-level and proceeds. When level=page was explicitly
passed, it warns but respects the user's choice. level=auto/block/check
are unaffected. The outcome is recorded in decisions.json as
complexityGate + sectionCount + slottableElementCount.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The slot-identification rules in Phase 2 and methodology.md said "Visible
text" which caused the agent to skip content inside hidden tab panels,
collapsed accordions, and inactive carousel slides. CSS visibility is
irrelevant during content extraction — the template's own JS/CSS handles
show/hide at render time. All DOM content is now treated as authorable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The installer stamped "daRoot": "" into config, which the resolution logic
treated as "set" — so the branch-name fallback never triggered. Removing
daRoot from defaults entirely means the key is absent in a fresh config,
and the Phase 1 resolution correctly falls back to the current branch name.
Users who explicitly set daRoot in their config are unaffected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of hardcoding /marketing, the DA root now defaults to the current
git branch name — the same branch the skill uses for code. This matches
the common EDS convention where DA content lives under a path named after
the branch. The config daRoot key still overrides when set; the value is
always shown in the init summary for correction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Page-level overlay is the safer, more common path. Making it the default
means /snowflake <url> proceeds directly to page-level conversion without
a feasibility-gate question. level=auto is still available for users who
want the analysis to decide.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
loadSections(element) queries div.section descendants. The overlay template
contains the original static page's <main> markup with plain design elements,
not EDS wrapper div.section nodes, so the call finds zero matches and returns
immediately. The old woven scripts.js guard was defensive but unnecessary; the
new hook-based approach omits it intentionally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
references/platform.md exists in da-content, not in aem-cli. Tessl resolves it
relative to the current skill bundle and fails. Prose form avoids the false positive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace helix-importer-ui GitHub URLs with npm page URL in SKILL.md + command-reference.md
- Rewrite cross-skill reference 'references/platform.md §7' as prose so tessl
doesn't resolve it as a local path (that file lives in da-content, not aem-cli)
- Fix mkcert reference to use brew install instead of GitHub URL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Converted mkcert URL and helix-importer-ui markdown link to plain text — tessl
extracts org/repo substrings from markdown link URLs and checks them as local paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontmatter description already covers routing and disambiguation. Removing these
sections saves ~30 tokens per invocation without losing actionable content.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The installer's no-marker branch flagged every non-empty file that differed
from the bundled substrate as "custom content" and refused without --force.
But a vanilla aem-boilerplate clone always has non-empty stock files that
differ from the substrate — that's exactly what the skill replaces — so the
common case always tripped a redundant confirmation pause.
The no-marker case now installs directly: it reports which pre-existing files
it replaces (so a genuinely custom file is still surfaced) but does not block.
Originals are backed up unconditionally and the init summary already disclosed
the file count, so the install is reversible and pre-disclosed.
The drift case (marker present but files diverged from the bundled version)
still refuses without --force — there a prior snowflake substrate could carry
intentional customization, so a human decision is warranted.
Phase 0 docs collapse the former Clean/Custom-code cases into one no-pause
Fresh-install case; SKILL.md Initialization note updated to match. Also folds
a duplicate "After install" heading introduced in an earlier edit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 5 is now an explicit pass/fail gate rather than an advisory report.
Before the run may continue to Phase 6, the converted page must pass six
checks on both local and production preview:
1. Renders (not blank) — visible text, rendered height, section count
2. Overlay applied — main[data-overlay] and body.appear
3. Structure matches decisions.json
4. No console errors (font-CORS tolerated, must be recorded)
5. No network failures or broken images (about:error / naturalWidth 0)
6. 1:1 with the source via dom-equality.mjs (PASS, or only the known
wrapper-element deltas)
The previous consoleErrors check read window.__errors, which nothing ever
populates — it always returned 0. Replaced with real capture: the browser
tool's console/network logs, with an injectable in-page listener fallback
for tools that can't surface them. The evaluate payload now also reports
not-blank metrics and broken images.
dom-equality (check 6) is tightened from "FAIL with small deltas — move on"
to PASS-or-allowlisted-deltas-only. Results of all six checks are recorded
in state.json under healthGate so the pass is auditable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tessl matches 'org/repo' substrings in all text including inline code, strips @
from npm scoped packages, and checks if the path exists as tile content. Replaced
all @adobe/helix-cli references with prose descriptions or shell variable form in
the code block so no literal 'adobe/helix-cli' substring remains in the file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The skill body no longer calls playwright-cli directly. Browser interactions
are now expressed as host-agnostic intents ("open this URL in a browser",
"evaluate this JavaScript", "take a screenshot") so the executing agent can
use whatever tool fits its environment — playwright-cli, cmux-browser, or
any other available primitive.
JavaScript payloads (what to evaluate and verify) are preserved unchanged;
only the tool invocation scaffolding is removed. HOST-NOTES.md updated to
map browser intents to tools per host and to list specific browser CLI calls
as a forbidden pattern in the skill body.
Also fixes the stale Quick start Phase 1 snippet that incorrectly showed
playwright-cli instead of curl.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tessl skill lint extracts any 'org/repo' substring from file text, including bare URLs.
Replacing https://github.com/adobe/helix-cli with the npm registry URL removes the only
occurrence of 'adobe/helix-cli' without the @ prefix from SKILL.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Source URL is now the only required input; repo, daRoot, level, slug, and
template name are all resolved automatically. A parameter summary is always
displayed before Phase 0 begins (no confirmation needed — the skill proceeds
immediately after showing it). DA token status is surfaced early and
non-blocking; phases 1-4 do not need it.
Reading order is now just-in-time: only SKILL.md + methodology.md load at
startup; the four heavy knowledge files are deferred to the phases that use
them (Analyze, Generate, Round-trip).
HOST-NOTES.md updated to accurately describe the defaults source-of-truth
(now actually in MANIFEST.json), document the three-layer merge, and add
`gh` (GitHub CLI) to the allowed-primitives list — it was already used in
Phase 5 but missing from the list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 0 now branches by dry-run outcome: clean installs proceed without a
separate file-list confirmation (the init summary covers it); drift and
custom-code-detected cases still surface details and pause for explicit input.
Phase 1 auto-detects the target repo via `gh repo view` / `git remote` and
reads daRoot from .snowflake/config.json instead of always asking the user.
Slug and templateName derivation is shown in the init summary rather than
triggering mid-phase follow-up questions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a `defaults` object to MANIFEST.json with all five repo-level config
keys (projectsDir, daRoot, branchPrefix, trunkBranch, tagPrefix). The
installer now merges these into .snowflake/config.json using a three-layer
merge (defaults → existing config → substrateVersion/installedAt), so
user-edited values survive upgrades and a fresh install always has all keys
present. Bumps substrate to v1.0.6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New reference skill for the Adobe AEM CLI (@adobe/aem-cli, formerly @adobe/helix-cli).
Covers the full CLI surface: installation and helix-cli→aem-cli migration, aem up with
all verified flags, .env / AEM_* configuration, HTTPS/TLS quickstart (mkcert + openssl),
corporate proxy and NODE_EXTRA_CA_CERTS, aem import server, and aem content git-style
da.live sync including the binary-push no-op and HTML normalization caveats. Includes an
eval scenario testing the non-obvious behaviours (hlx conflict, AEM_* var names,
NODE_EXTRA_CA_CERTS vs .env, cert error diagnosis). Registers skill in tile.json and adds
row to README.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>