Skip to content

A2UI v0.1 + Fordham Hill tenant (spec + 7 components + compose tool + conformance + tenant page) - #2132

Merged
POWERFULMOVES merged 10 commits into
mainfrom
feat/a2ui-v01-fordham-hill
Jul 18, 2026
Merged

A2UI v0.1 + Fordham Hill tenant (spec + 7 components + compose tool + conformance + tenant page)#2132
POWERFULMOVES merged 10 commits into
mainfrom
feat/a2ui-v01-fordham-hill

Conversation

@POWERFULMOVES

@POWERFULMOVES POWERFULMOVES commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • docs(website): baseline + brand asset family — Playwright + axe-core baseline of website/ + /stage/; 5 expected 404s (founder TODOs); 2 real color-contrast violations identified (CSS-fix PR candidate, separate); 4 brand assets generated (og-image, favicon, apple-touch-icon, mobile-node rig)
  • docs(agnote): CLAIM WEBSITE_AS_AGENT_CANVAS — DARKXSIDE assigned Mavis-5090 architectural leadership for the lane
  • docs(contracts): A2UI v0.1 spec — HTML5 Web Components contract, data-binding model C (push + single-pull, no chains), 7 component recipes, 15 persona theming tokens, ARIA rules
  • feat(a2ui): v0.1 first slice — 3 components (<pm-space-agent-card>, <pm-project-card>, <pm-metric-tile>) + compose_tenant_page() Python tool + conformance test (19/19 Python tests, 0 axe-core violations)
  • feat(a2ui): complete v0.1 registry + Fordham Hill tenant — 4 more components (<pm-timeline>, <pm-voice-clip>, <pm-image>, <pm-quote-block>) + website/tenant-template/ + Fordham Hill composed tenant page live

The headline deliverable: the Fordham Hill Co-op tenant page is a working CF Pages-shaped site composed via compose_tenant_page() from a JSON fixture. The "CF Pages is a canvas PMOVES agents paint on" reframe is proven.

Testing

# Python tests (compose tool + fixtures)
python -m pytest pmoves/tools/compose/tests/test_compose.py -v
# 19/19 PASS

# A2UI v0.1 conformance (in-browser harness, axe-core 4.10.2)
# Open pmoves/contracts/a2ui-v0.1-conformance.test.html in browser
# Result: 7/7 components PASS, axe-core 0 violations, 21 rules passed

# Live Fordham Hill tenant page render
# Open website/tenant-template/index.html in browser
# 18 A2UI messages / 16 components across all 7 v0.1 types

Required Checks

  • CHIT Contract Check — pass (no contract changes; new contracts added under pmoves/contracts/a2ui-v0.1.md)
  • Updated contracts, schemas, or topics — new spec pmoves/contracts/a2ui-v0.1.md + new conformance test pmoves/contracts/a2ui-v0.1-conformance.test.html
  • Added/updated documentationpmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md + AGNOTE trail entries

Evidence directory

pmoves/docs/evidence/website-baseline-2026-07-14/ — baseline screenshots, conformance runs, brand assets, tenant page render, demo HTML files for each component.

Review Coordination

  • Requested Codex review — please run /chit:review-sweep or equivalent after PR opens
  • Requested GitHub Copilot review — use the PR "Copilot" button

Follow-up Tasks

  • Real Fordham resident review of the Fordham Hill tenant page (legitimacy, copy tone, voice-clip sample)
  • Sign-off on the 4 generated brand assets before promoting to website/og-image.png + website/assets/{favicon.ico,apple-touch-icon.png,mobile-node-rig.jpg}
  • CSS fix PR for the 2 identified color-contrast violations (1-3 line change, <15 min effort, low-risk)
  • Wire the v0.1 conformance test into CI (currently manual in-browser)

Reviewer Notes

Architectural decisions to scrutinize:

  • HTML5 Web Components (Custom Elements + Shadow DOM) as substrate, no framework. Lit fine for renderer (website/stage/vendor/a2ui.mjs); the library is plain Web Components so any agent in any language can emit them.
  • Data-binding model C (push via props + single data-source pull; no chained pull). The "single pull" rule is the strict subset that keeps conformance simple. v0.2 unlocks chained pull additively.
  • ARIA attrs on HOST elements (not just shadow root) because axe-core doesn't pierce shadow DOM by default.
  • 15 --pm-* CSS custom properties for persona theming. Components that hardcode colors fail conformance.

For the reviewer on a fresh node: the conformance test (pmoves/contracts/a2ui-v0.1-conformance.test.html) is the single source of truth. If a component passes there, ship it. If it fails, fix it before reviewing other concerns.

For the local-model-on-Spark-Knuckles reviewer: start with pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md for the original baseline; then read pmoves/contracts/a2ui-v0.1.md for the contract; then run the conformance test in a browser; then look at the 7 components + the Fordham Hill tenant page in website/tenant-template/.

Pre-PR self-check (PR_NOTES.md):

  • Python tests 19/19 pass
  • A2UI conformance 7/7 pass
  • axe-core WCAG 2 AA: 0 violations
  • 5-class classifier applied to my own diff: caught 2 issues before pushing (role prop → renamed agentRole/attributionRole to avoid ARIA attr conflict; ARIA attrs on host not shadow root)
  • AGNOTE CLAIM + RELEASE rows written

Worked-example callout: a fix-pattern from this PR is the "ARIA attrs on host" rule. It's now codified in a2ui-v0.1.md §6.2 and tested in conformance. Future PRs that violate it will fail conformance — that's the desired behavior.

Sister PRs:

  • PR 2 (this stack): feat/a2ui-v02-design — pm-haptic + v0.2 ballot spec DRAFT
  • PR 3 (this stack): feat/a2ui-v02-impl-review-style — pm-toast + pm-ballot + St. Maarten tenant + v0.2 event wire + CF Pages deploy + review-style scaffolding

See also

  • pmoves/contracts/a2ui-v0.1.md — the spec
  • pmoves/contracts/a2ui-v0.1-conformance.test.html — the conformance test (run this in a browser to verify)
  • pmoves/web-components/README.md — component recipe index
  • pmoves/tools/compose/README.md — compose tool docs
  • pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md — original baseline
  • pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md — claim/release register (CLAIM at Mavis-5090::WEBSITE-AS-AGENT-CANVAS-CLAIM::2026-07-15, RELEASE at Mavis-5090::A2UI-V0.1-FIRST-SLICE-SHIPPED::2026-07-15 + Mavis-5090::WEBSITE-AS-AGENT-CANVAS-PARALLEL-BUILD::2026-07-15)

Summary by CodeRabbit

  • New Features
    • Added seven reusable A2UI v0.1 web components for images, quotes, timelines, voice clips, metrics, projects, and space agents.
    • Added tenant-page rendering with theme support and a Fordham Hill example page.
    • Added a composition tool for generating validated tenant page content.
  • Documentation
    • Added A2UI v0.1 specifications, component usage guides, and registry documentation.
  • Tests
    • Added browser-based conformance checks for registration, theming, encapsulation, and accessibility.
    • Added automated accessibility, link, screenshot, and composition validation evidence.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@POWERFULMOVES, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 848661f4-a642-44ff-af42-85ac65d77f17

📥 Commits

Reviewing files that changed from the base of the PR and between 38adaf9 and bb49bcb.

⛔ Files ignored due to path filters (17)
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/apple-touch-icon-1024.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/favicon-512.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/mobile-node-rig.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/og-image.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-7-components.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-screenshot.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-image.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-quote-block.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-timeline.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-voice-clip.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-desktop.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-mobile.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-tablet.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-desktop.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-mobile.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-tablet.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/tenant-fordham-hill.png is excluded by !**/*.png
📒 Files selected for processing (12)
  • pmoves/contracts/a2ui-v0.1-conformance.test.html
  • pmoves/contracts/a2ui-v0.1.md
  • pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md
  • pmoves/docs/evidence/website-baseline-2026-07-14/a11y-detail.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/all-7-screenshot.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/all-7-screenshots.json
  • pmoves/docs/evidence/website-baseline-2026-07-14/baseline-results.json
  • pmoves/docs/evidence/website-baseline-2026-07-14/baseline.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-run.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/http-server.err
  • pmoves/docs/logs/pr_trim_2132_LEARNINGS.md
  • pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md
📝 Walkthrough

Walkthrough

This change defines A2UI v0.1, adds seven Shadow DOM web components, introduces tenant composition and rendering for Fordham Hill, and adds conformance, accessibility, screenshot, broken-link, and operational evidence tooling.

Changes

A2UI tenant canvas

Layer / File(s) Summary
Contract and conformance harness
pmoves/contracts/*
Defines A2UI v0.1 component, binding, theming, accessibility, lifecycle, and conformance rules with an in-browser validation harness.
Web components and registry
pmoves/web-components/*
Adds seven registered components with demos and documentation, including Shadow DOM rendering, escaping, ARIA semantics, CSS tokens, and live data handling.
Composition tool
pmoves/tools/compose/*
Adds schemas, validation, message composition, JSON serialization, Fordham Hill fixtures, and tests.
Tenant rendering
website/tenant-template/*
Adds the tenant page shell, renderer, themed stylesheet, and Fordham Hill message payload.
Evidence and records
pmoves/docs/evidence/*, pmoves/docs/operations/*, pmoves/docs/logs/*, pmoves/docs/AGENTS/*
Adds Playwright and axe-core evidence scripts, manifests, captured results, operational documentation, and delivery records.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the PR’s main scope: A2UI v0.1 spec, seven components, compose tool, conformance, and Fordham Hill tenant page.
Description check ✅ Passed The description includes the required Summary, Testing, Required Checks, Review Coordination, Follow-up Tasks, and Reviewer Notes sections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/a2ui-v01-fordham-hill

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs Documentation governance AGNOTE register / agent definitions / damage-control hooks labels Jul 15, 2026
POWERFULMOVES pushed a commit that referenced this pull request Jul 15, 2026
…OTE claim

3 PRs pushed and opened against main, per DARKXSIDE's 'ok to proceed' on
the 3-PR split:

  PR #2132 - A2UI v0.1 + Fordham Hill tenant
    head: feat/a2ui-v01-fordham-hill (5 commits, f45687e)
    body: pmoves/docs/logs/pr_open/PR_1_v01_fordham_hill.body.md

  PR #2133 - A2UI v0.2 design - pm-haptic + v0.2 ballot spec (DRAFT)
    head: feat/a2ui-v02-design (3 commits, a9046e4)
    body: pmoves/docs/logs/pr_open/PR_2_v02_design.body.md

  PR #2134 - A2UI v0.2 implementation + review-style scaffolding
    head: feat/a2ui-v02-impl-review-style (4 commits, 1a85215)
    body: pmoves/docs/logs/pr_open/PR_3_v02_impl_review_style.body.md

Original worktree branch feat/auto-20260714-9d8a9584 also pushed
as a reference; 30 commits behind main (DARKXSIDE chose push-as-is
over rebase; merge is his call, not a push-time decision).

This commit adds:
- AGNOTE CLAIM for the PR-review-trim cycle (will be released when
  all 3 PRs are reviewed + LEARNINGS.md artifacts filled + threads
  resolved)
- pr_manifest_2026-07-15.json: PR numbers, head SHAs, commit lists,
  learnings paths, reviewer targets, AGNOTE links. Lets Spark/Knuckles
  next session see exactly what was opened + where the trim artifacts
  will land
- The 3 PR body files (these are the bodies that went into GitHub;
  committed here so future agents can see exactly what the reviewers
  were given, plus for cross-reference from the LEARNINGS.md when
  reviews come back)

Standing applicability: the trim cycle from
pmoves/docs/operations/REVIEW_STYLE_2026-07-15.md applies to all 3 PRs.
The 5-class taxonomy + 4 LEARNINGS buckets + conformance gate +
6-source triangulation are the same; only the conformance commands
+ evidence dirs differ per PR.

Refs:
- pmoves/docs/operations/REVIEW_STYLE_2026-07-15.md (trim style)
- pmoves/docs/templates/PR_LEARNINGS.template.md (bucket template)
- .claude/hooks/a2ui-crew-trail.sh (lane trail hook)
- pmoves/docs/logs/pr_open/pr_manifest_2026-07-15.json (the manifest)
- pmoves/docs/logs/pr_open/PR_{1,2,3}_*.body.md (PR bodies)
- pmoves/tools/pr_hedge_trim.py (python tool, no make wrapper on
  this branch)

AGNOTE rows:
- Mavis-5090::PR-REVIEW-TRIM-CYCLE-CLAIM::2026-07-15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f45687ea0f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +88
const href = this._escapeAttr(link.href || '#');
const external = (link.href || '').startsWith('http');
return `<a class="link" href="${href}" ${external ? 'rel="noopener noreferrer" target="_blank"' : ''}>${label} →</a>`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject unsafe project link protocols

When an agent/tenant payload supplies a project link with a javascript: URL, this code only HTML-escapes the string and then writes it into href, so clicking the rendered action link executes script in the tenant page. Because these links are fed from A2UI JSON/compose props, escaping is not enough; sanitize or allowlist protocols such as relative URLs, http(s), and approved mail links before rendering.

Useful? React with 👍 / 👎.

}

_subscribeNats(subject) {
const url = `/api/nats/sse?subject=${encodeURIComponent(subject)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use the existing SSE route for NATS sources

For any non-HTTP data-source such as the documented fordham:mesh.uptime, this opens /api/nats/sse?subject=...; after checking the repo routes with rg "api/nats|sse/events", the NATS SSE bridge exposed here is pmoves/services/showtime-api/app.py's /sse/events, and it emits named SSE events. As written, live NATS-backed metric tiles (and the same pattern in pm-space-agent-card) hit a route that is not present in this app and fall into the error state instead of updating.

Useful? React with 👍 / 👎.

POWERFULMOVES pushed a commit that referenced this pull request Jul 16, 2026
…no polling

DARKXSIDE (2026-07-15): 'github sends notifications why not listen
for those this way no need to poll'. This ships the A-mode
implementation: a long-running listener that uses HTTP conditional
GET (ETag) on GitHub's /notifications API. When nothing has changed,
GitHub returns 304 instantly. When a new notification arrives, the
full payload comes back in the same call. Cost of 'listening' is
essentially zero until something happens.

Three modes (same CLI, same events):

  notifications (default): gh api /notifications + ETag, with
    exponential backoff (30s -> 300s cap) on 304. The 'just listen'
    mode — works with no public webhook URL, no NATS, no infra.
    Filters to PR-related notifications + optional --prs list.

  pr-watch: per-PR review state polling with diff. Emits when
    reviewDecision, state, latestReviews, or comments change.
    More precise than notifications for catching inline review
    threads. Same tool, just a different transport.

  nats (B mode, ships later): subscribe to github.webhook.review.*
    subjects from the n8n webhook bridge. Same CLI, no behavior
    change at the consumer. KiloCode is bringing up n8n per
    DARKXSIDE 2026-07-16.

Why this matters for the review-trim cycle: Mavis-5090 was
implicitly assuming periodic gh pr list polls to detect when
reviews came in. That's wasteful — GitHub already pushes
notifications. The watcher surfaces review events the moment
they arrive, the log file (pmoves/docs/logs/pr_review_arrivals.jsonl)
is the durable feed, and --emit-nats broadcasts to other agents
on chit.pr.review.detected.v1.

Deliverables in this commit:

  pmoves/tools/pr_review_watcher.py (21KB, 350 lines, stdlib only)
    - run() helper hardened for Windows subprocess + UTF-8
    - gh_notifications() with ETag via gh api --include
    - gh_pr_state() per-PR review state via gh pr view --json
    - notif_is_pr_review() filters to PR-related notifications
    - state_diff() computes changes between PR states
    - emit() with stdout + JSONL + NATS publish
    - Three mode functions: mode_notifications / mode_pr_watch /
      mode_nats (with nats-py + nats CLI fallbacks)
    - Graceful SIGINT (exit 130), timeout (exit 1), error (exit 2)
    - Tested end-to-end: notifications mode found 2 existing
      author notifications on PRs #2133 + #2134 and timed out
      cleanly. pr-watch mode detected PR #2132 initial state
      (reviewDecision=null, 1 latestReview, 1 comment) and timed
      out cleanly.

  pmoves/tools/pr_review_watcher.README.md (6.4KB)
    - Why this exists (the 'no polling' framing)
    - All three modes documented with examples
    - Event format spec (3 event kinds: notification, review_state_change, nats_message)
    - Exit code table
    - NATS integration (publish subject + fallback chain)
    - A-to-B migration section (same CLI, --mode nats later)
    - See-also links to the meta-doc + LEARNINGS template

  pmoves/mk/pr-review.mk (4.4KB)
    - pr-review-watch (foreground)
    - pr-review-watch-mode (single-PR / one-shot)
    - pr-review-watch-daemon (nohup background, 7-day max-runtime)
    - pr-review-tail (tail the JSONL log - this IS subscribing)
    - pr-review-status (last 5 arrivals + watcher PID + NATS subject)
    - pr-review-trim (call pmoves/tools/pr_hedge_trim.py after event)
    - pr-review-help (target self-doc)

  pmoves/Makefile (modified, +1 line)
    - Added 'include mk/pr-review.mk' after voice.mk

  pmoves/docs/logs/pr_open/pr_manifest_2026-07-15.json (modified)
    - Added 'monitoring_strategy' block with the A-mode and B-mode
      paths, the log path, the outbound NATS subject. Future agents
      reading the manifest can see the full detection strategy.

Standing applicability: the watcher is the listener for the trim
cycle. When it detects a review event on a watched PR, the log
line tells Mavis-5090 (or any other agent) which PR + which user +
which kind of review, and the trim cycle can be triggered manually
or via a follow-up NATS subscriber (B mode).

AGNOTE rows (to follow):
- Mavis-5090::PR-REVIEW-WATCHER-A-MODE-DELIVERED::2026-07-15

Refs:
- pmoves/tools/pr_review_watcher.py
- pmoves/tools/pr_review_watcher.README.md
- pmoves/mk/pr-review.mk
- pmoves/docs/operations/REVIEW_STYLE_2026-07-15.md (the meta-doc this feeds)
- pmoves/docs/templates/PR_LEARNINGS.template.md (the bucket template)
- pmoves/n8n/flows/github_webhook_processor.json (B mode source, owned by KiloCode)
POWERFULMOVES pushed a commit that referenced this pull request Jul 16, 2026
Four defense-in-depth gaps between the compose-tool validation and the
runtime renderer, which fetches tenant JSON without it:

- pm-image: aspect-ratio is interpolated into the shadow <style> block;
  constrain to the spec's <int>/<int> shape so it can never carry CSS/HTML
  out of that context.
- pm-project-card: _escapeAttr stops attribute breakout but not scheme
  abuse; javascript: hrefs now render as inert '#' (http/https/mailto/
  relative allowed).
- tenant-renderer applyProps: only props declared in the component's
  observedAttributes are applied — undeclared keys (innerHTML, onclick, …)
  are dropped with a console warning.
- tenant-renderer createComponent: registry components (pm-*, defined in
  customElements) only; ?tenant= values are slug-validated in both the
  renderer and index.html before becoming a fetch path.

Conformance after fix: 7/7 registered, tokens PASS, shadow DOM PASS,
axe-core 0 violations (21 rules) — identical to pre-trim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POWERFULMOVES pushed a commit that referenced this pull request Jul 16, 2026
…2132)

The shipping tenant page loaded images.unsplash.com and archive.org —
website/ must self-host everything (visitor IPs were leaking to third
parties, and the stock photo was credited 'Photo: DARKXSIDE').

- pm-image: swap to ./assets/fordham-gateway.jpg — the Mavis-5090
  generated mobile-node-rig render (evidence dir), downscaled 800px/70KB,
  credited honestly as a generated render pending sign-off.
- pm-voice-clip: swap the archive.org test mp3 (labeled 2:14, actually
  ~12s of test tone) for a 4s locally generated placeholder, titled and
  timed honestly until the real clip is recorded.
- Fixed in the fixture AND regenerated via compose_fordham_demo.py so the
  composer can't resurrect the CDN refs. Zero external URLs remain.

Dev-only demo.html files under pmoves/web-components/ still use CDN
avatars; out of scope here (not website/), flagged in review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POWERFULMOVES pushed a commit that referenced this pull request Jul 16, 2026
- register.js: comment claimed a try/catch made re-registration safe;
  no try/catch exists — the real guard is ES-module caching, and
  customElements.define throws on duplicates. Say so.
- index.html: <meta name="color-scheme" "dark"> was missing content=,
  parsing as a bogus boolean attribute; color-scheme never applied.
- test_compose.py: 'role' prop renamed to 'agentRole' in 4 places —
  the component deliberately renamed it to avoid clobbering the host
  ARIA role, but the tests still exercised the old name (latent drift;
  the hardened applyProps would now drop 'role' at runtime).

19/19 compose tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POWERFULMOVES pushed a commit that referenced this pull request Jul 16, 2026
…TYLE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@POWERFULMOVES POWERFULMOVES left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Peer-CLAUDE review (5090-CLAUDE) — 3-angle reciprocity, learnings-first per REVIEW_STYLE_2026-07-15.md

Verdict: solid v0.1 foundation — approve once the pushed trim commits are accepted. All findings below were verified against the branch and fixed directly on this branch (repo culture: fix PR threads ourselves; operator-directed). Full four-bucket detail in pmoves/docs/logs/pr_trim_2132_LEARNINGS.md (committed here).

Fixed on this branch (4 commits)

  1. XSS surface hardening missed-signal — the compose tool validates, but tenant-renderer.js fetches JSON and applies props without it, so the component layer is the runtime boundary. Closed: CSS-context injection via aspect-ratio into the shadow <style> (pm-image); javascript: hrefs surviving _escapeAttr (pm-project-card); applyProps writing any key incl. innerHTML onto live elements (now gated by each component's observedAttributes); createComponent instantiating arbitrary tags incl. <script> (now pm-* registry only); ?tenant= path traversal (slug-validated in renderer + index.html).
  2. No-CDN rule missed-signal — shipping tenant JSON loaded unsplash + archive.org (and credited a stock photo 'Photo: DARKXSIDE'). Swapped to self-hosted: the Mavis-5090 generated rig render (downscaled 70KB, honestly credited as pending sign-off) + a locally generated placeholder clip with honest duration. Fixed in the fixture and regenerated so the composer can't resurrect them. Dev-only demo.html CDN avatars left as-is (outside website/) — flagging as optional follow-up.
  3. Correctness nits fix-pattern/wrong-suggestion — register.js comment claimed a try/catch that doesn't exist (real guard = ES-module caching); malformed <meta name="color-scheme">; roleagentRole swept through tests (4 latent sites).

Verified true (credit)

19/19 compose tests (ran them); _escapeText/_escapeAttr correct in all 7 components for HTML/attr contexts; lifecycle cleanup correct; no secrets/topology; ✦ --pm-signature untouched. Conformance after trim: 7/7 registered, axe-core 0 violations (21 rules), full tenant render, clean console — identical to pre-trim.

Merge strategy (operator preference: atomic commits, no accidental cascade)

The three A2UI branches are commit-stacked (2132 ⊂ 2133 ⊂ 2134) but all target main. Merge in order 2132 → 2133 → 2134, rebasing each next branch onto main after the previous squash-merge — otherwise the squash re-diffs the whole stack. Fix commits were landed on the branch whose increment owns each file, so they compose cleanly under that order.

Suggested gate addition missed-signal

The conformance harness checks a11y/tokens/shadow but not origins — a one-line grep -E 'https?://' website/tenant-template/data/*.json check would have caught the CDN refs at build time.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 16

🧹 Nitpick comments (1)
pmoves/contracts/a2ui-v0.1.md (1)

255-255: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language identifier to the fenced code block.

Specifying a language identifier improves syntax highlighting and resolves a markdown lint warning.

♻️ Proposed refactor
-```
+```text
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/contracts/a2ui-v0.1.md` at line 255, Update the fenced code block at
the referenced location in a2ui-v0.1.md to include the text language identifier,
changing the opening fence to use ```text while preserving the block’s contents
and formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pmoves/contracts/a2ui-v0.1-conformance.test.html`:
- Around line 146-147: Update the hardcoded color detection near usesTokens and
hasHardcodedHex to remove or ignore complete var() usages, including their
fallback values, before applying the hexadecimal color regex. Preserve detection
of hex colors outside custom-property var() expressions and keep the existing
conformance warning behavior.

In `@pmoves/contracts/a2ui-v0.1.md`:
- Around line 114-134: Update the A2UI contract’s JavaScript and JSON examples
at pmoves/contracts/a2ui-v0.1.md:114-134 to use agentRole instead of role. Also
update the component recipes table at pmoves/contracts/a2ui-v0.1.md:219-228 to
list agentRole for pm-space-agent-card and attributionRole? for pm-quote-block.
- Around line 68-74: Update section 4.3 Registration to document the central
registry at pmoves/web-components/register.js, which imports and registers all
shipped components. Remove the claims that components self-register through
individual register.js files, that the renderer imports each component
registration on demand, and that no global registry exists.

In `@pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md`:
- Around line 151-158: Update the Playwright baseline command in the documented
procedure to use repository-relative or environment-independent paths instead of
the hardcoded Windows user and worktree paths, and invoke the runner from
~/.openclaw-autoclaw/skills/. Keep the local server command and baseline.js
target unchanged in purpose while ensuring the instructions work across
supported platforms.

In `@pmoves/tools/compose/compose.py`:
- Around line 319-321: Update the component composition loop handling props for
each comp so an explicitly null comp.get("props") falls back to an empty
dictionary, preserving the existing behavior for provided property mappings and
preventing downstream TypeErrors.
- Around line 233-234: Update the props assignment in the composition-processing
flow to use an empty dictionary when the configuration’s props value is missing
or explicitly null. Preserve the existing component lookup and ensure downstream
iteration and membership checks always receive a mapping.
- Around line 298-300: Update the configuration handling around tenant_meta and
components so explicit null values for "tenant" and "components" fall back to
their empty defaults before dict conversion or iteration. Preserve the existing
behavior for populated tenant metadata and component lists.

In `@pmoves/tools/compose/tests/fixtures/fordham-hill.composed.json`:
- Around line 34-42: Regenerate the composed tenant outputs by running
compose_fordham_demo.py so both
pmoves/tools/compose/tests/fixtures/fordham-hill.composed.json (lines 34-42) and
website/tenant-template/data/fordham-hill.json (lines 34-42) use the self-hosted
media paths from the fordham-hill.json fixture instead of external URLs, then
commit the generated changes.

In `@pmoves/web-components/pm-metric-tile/pm-metric-tile.js`:
- Around line 138-139: Replace the literal trend colors in
pmoves/web-components/pm-metric-tile/pm-metric-tile.js lines 138-139 with the
documented themeable custom-property tokens while preserving the up/down trend
mapping. Update the token-only claim in
pmoves/web-components/pm-metric-tile/README.md lines 5-6 to match the
implementation, and update or remove the registry-wide conformance claim in
pmoves/web-components/README.md lines 81-87 unless the implementation now
satisfies it.
- Around line 36-42: Update connectedCallback and _syncHostAria so arbitrary or
unbounded KPI values are not exposed with fabricated meter semantics: use a
neutral role and avoid announcing nonnumeric values as zero or negative values
below a fixed minimum. Apply role="meter" and
aria-valuemin/aria-valuemax/aria-valuenow only when explicit valid bounds exist,
preserving the existing bounded-meter behavior.
- Around line 221-228: Update each data-handling path around _applyData so it
marks the component live only when the payload contains the required value
field; treat missing or malformed value data as an error and avoid applying it.
Apply the same validation consistently to the additional paths identified in the
comment, while preserving the existing render flow.

In `@pmoves/web-components/pm-project-card/pm-project-card.js`:
- Around line 83-88: Update the link rendering in the map callback to normalize
and validate link.href through _safeHref before determining whether it is
external. Ensure the resulting href permits only http:, https:, mailto:, and
relative or fragment URLs, rejecting obfuscated scheme-less javascript values,
and perform the external scheme check on the normalized href.

In `@pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js`:
- Around line 197-217: Update _pollHttp in
pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js at lines
197-217 to make its returned cancel handle abort or invalidate the in-flight
presence request, preventing stale responses from updating presence. Apply the
same cancellation or invalidation behavior to the metric request in
pmoves/web-components/pm-metric-tile/pm-metric-tile.js at lines 208-235; both
components must prevent obsolete source requests from overwriting current state.
- Around line 24-25: Standardize the agent-role contract on agentRole/agent-role
throughout the pm-space-agent-card component and documentation. In
pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js lines 24-25,
observe agent-role; update the HTML example at
pmoves/web-components/pm-space-agent-card/README.md lines 12-19, JavaScript
usage at lines 24-30, A2UI prop at lines 35-46, and documented properties at
lines 53-54 to use agentRole or agent-role instead of role; update the registry
example in pmoves/web-components/README.md lines 27-35 accordingly.

In `@pmoves/web-components/pm-timeline/pm-timeline.js`:
- Around line 16-20: Add the explicit role="list" attribute to the inner <ol>
rendered by pm-timeline’s render method, preserving the existing list structure
and styles so the shadow-root list retains accessible semantics across browsers.

In `@pmoves/web-components/README.md`:
- Around line 68-76: Update the component setup instructions around the
per-component register.js step to direct contributors to register new components
in the top-level pmoves/web-components/register.js registry, matching the
self-registration and full-registry import flow. Remove the inaccurate
per-component registration guidance while preserving the remaining schema,
supported-component, and conformance-test steps.

---

Nitpick comments:
In `@pmoves/contracts/a2ui-v0.1.md`:
- Line 255: Update the fenced code block at the referenced location in
a2ui-v0.1.md to include the text language identifier, changing the opening fence
to use ```text while preserving the block’s contents and formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59d723a9-90bb-484a-a2a8-cd476a45751c

📥 Commits

Reviewing files that changed from the base of the PR and between 5c854f8 and 38adaf9.

⛔ Files ignored due to path filters (19)
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/apple-touch-icon-1024.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/favicon-512.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/mobile-node-rig.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/assets-generated/og-image.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-7-components.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-screenshot.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-image.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-quote-block.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-timeline.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/demo-pm-voice-clip.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-desktop.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-mobile.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/index-tablet.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-desktop.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-mobile.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/screenshots/stage-tablet.png is excluded by !**/*.png
  • pmoves/docs/evidence/website-baseline-2026-07-14/tenant-fordham-hill.png is excluded by !**/*.png
  • website/tenant-template/assets/fordham-gateway.jpg is excluded by !**/*.jpg
  • website/tenant-template/assets/placeholder-voice.mp3 is excluded by !**/*.mp3
📒 Files selected for processing (46)
  • pmoves/contracts/a2ui-v0.1-conformance.test.html
  • pmoves/contracts/a2ui-v0.1.md
  • pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md
  • pmoves/docs/evidence/website-baseline-2026-07-14/a11y-detail.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/all-7-screenshot.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/all-7-screenshots.json
  • pmoves/docs/evidence/website-baseline-2026-07-14/baseline-results.json
  • pmoves/docs/evidence/website-baseline-2026-07-14/baseline.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/conformance-run.js
  • pmoves/docs/evidence/website-baseline-2026-07-14/http-server.err
  • pmoves/docs/logs/pr_trim_2132_LEARNINGS.md
  • pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md
  • pmoves/tools/compose/__init__.py
  • pmoves/tools/compose/compose.py
  • pmoves/tools/compose/compose_fordham_demo.py
  • pmoves/tools/compose/tests/__init__.py
  • pmoves/tools/compose/tests/fixtures/fordham-hill.composed.json
  • pmoves/tools/compose/tests/fixtures/fordham-hill.json
  • pmoves/tools/compose/tests/test_compose.py
  • pmoves/web-components/README.md
  • pmoves/web-components/pm-image/README.md
  • pmoves/web-components/pm-image/demo.html
  • pmoves/web-components/pm-image/pm-image.js
  • pmoves/web-components/pm-metric-tile/README.md
  • pmoves/web-components/pm-metric-tile/demo.html
  • pmoves/web-components/pm-metric-tile/pm-metric-tile.js
  • pmoves/web-components/pm-project-card/README.md
  • pmoves/web-components/pm-project-card/demo.html
  • pmoves/web-components/pm-project-card/pm-project-card.js
  • pmoves/web-components/pm-quote-block/README.md
  • pmoves/web-components/pm-quote-block/demo.html
  • pmoves/web-components/pm-quote-block/pm-quote-block.js
  • pmoves/web-components/pm-space-agent-card/README.md
  • pmoves/web-components/pm-space-agent-card/demo.html
  • pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js
  • pmoves/web-components/pm-timeline/README.md
  • pmoves/web-components/pm-timeline/demo.html
  • pmoves/web-components/pm-timeline/pm-timeline.js
  • pmoves/web-components/pm-voice-clip/README.md
  • pmoves/web-components/pm-voice-clip/demo.html
  • pmoves/web-components/pm-voice-clip/pm-voice-clip.js
  • pmoves/web-components/register.js
  • website/tenant-template/data/fordham-hill.json
  • website/tenant-template/index.html
  • website/tenant-template/tenant-renderer.js
  • website/tenant-template/tenant.css

Comment on lines +146 to +147
const usesTokens = /var\(--pm-/.test(cssText);
const hasHardcodedHex = /#[0-9a-fA-F]{3,8}/.test(cssText);

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Exclude var() fallbacks from the hardcoded hex color check.

The current regex flags legitimate hex colors provided as fallback values inside CSS custom properties (e.g., var(--pm-bg, #0b0b10)). This produces misleading [HARDCODED HEX!] warnings in the conformance output. Stripping var() usages before matching the regex prevents these false positives.

💚 Proposed fix
-      const usesTokens = /var\(--pm-/.test(cssText);
-      const hasHardcodedHex = /#[0-9a-fA-F]{3,8}/.test(cssText);
+      const usesTokens = /var\(--pm-/.test(cssText);
+      const hasHardcodedHex = /#[0-9a-fA-F]{3,8}/.test(cssText.replace(/var\([^)]+\)/g, ''));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const usesTokens = /var\(--pm-/.test(cssText);
const hasHardcodedHex = /#[0-9a-fA-F]{3,8}/.test(cssText);
const usesTokens = /var\(--pm-/.test(cssText);
const hasHardcodedHex = /#[0-9a-fA-F]{3,8}/.test(cssText.replace(/var\([^)]+\)/g, ''));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/contracts/a2ui-v0.1-conformance.test.html` around lines 146 - 147,
Update the hardcoded color detection near usesTokens and hasHardcodedHex to
remove or ignore complete var() usages, including their fallback values, before
applying the hexadecimal color regex. Preserve detection of hex colors outside
custom-property var() expressions and keep the existing conformance warning
behavior.

Comment on lines +68 to +74
### 4.3 Registration

- Components self-register via `customElements.define()`
- Registration script: `pmoves/web-components/<name>/register.js` (imports the component + calls `customElements.define`)
- The renderer (Lit-based) imports `register.js` for each component it needs
- No global component registry required — components are imported on demand

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the registration documentation with the actual file structure.

The specification states that there is no global component registry and that each component has its own register.js. However, the file structure outlined on Line 278 and the PR summary confirm the use of a central registry at pmoves/web-components/register.js that imports and registers all shipped components. Please update this section to reflect the actual architecture.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/contracts/a2ui-v0.1.md` around lines 68 - 74, Update section 4.3
Registration to document the central registry at
pmoves/web-components/register.js, which imports and registers all shipped
components. Remove the claims that components self-register through individual
register.js files, that the renderer imports each component registration on
demand, and that no global registry exists.

Comment on lines +114 to +134
```javascript
const card = document.createElement('pm-space-agent-card');
card.agentName = 'CLAUDE-OPUS';
card.role = 'analytical';
card.presence = 'live';
document.body.appendChild(card);
```

Or via the A2UI message stream:

```json
{
"type": "createComponent",
"component": "pm-space-agent-card",
"props": {
"agentName": "CLAUDE-OPUS",
"role": "analytical",
"presence": "live"
}
}
```

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Update the spec to reflect ARIA-safe property renames.

The A2UI specification contract is out of sync with the actual component implementations and the learnings documented in pr_trim_2132_LEARNINGS.md. The role properties were renamed in the components to avoid clobbering the HTML ARIA role attribute, but the contract still references the old names, which will cause developers to author broken data-bindings.

  • pmoves/contracts/a2ui-v0.1.md#L114-L134: Update the JavaScript and JSON data-binding examples to use agentRole instead of role.
  • pmoves/contracts/a2ui-v0.1.md#L219-L228: Update the component recipes table to use agentRole for <pm-space-agent-card> and attributionRole? for <pm-quote-block>.
📍 Affects 1 file
  • pmoves/contracts/a2ui-v0.1.md#L114-L134 (this comment)
  • pmoves/contracts/a2ui-v0.1.md#L219-L228
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/contracts/a2ui-v0.1.md` around lines 114 - 134, Update the A2UI
contract’s JavaScript and JSON examples at pmoves/contracts/a2ui-v0.1.md:114-134
to use agentRole instead of role. Also update the component recipes table at
pmoves/contracts/a2ui-v0.1.md:219-228 to list agentRole for pm-space-agent-card
and attributionRole? for pm-quote-block.

Comment on lines +151 to +158
```bash
# 1. Start the local server (from repo root)
python -m http.server 8765 --bind 127.0.0.1 --directory website

# 2. Run the playwright baseline (separate shell)
node "C:\Users\russe\.agents\skills\playwright\run.js" \
"C:\Users\russe\Documents\GitHub\PMOVES.AI\.worktrees\feat-auto-20260714-9d8a9584\pmoves\docs\evidence\website-baseline-2026-07-14\baseline.js"
```

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use platform-agnostic paths and the correct skill directory.

The command instructions use hardcoded absolute Windows paths specific to a single user profile (C:\Users\russe\...). As per path instructions, we must flag command drift across Windows/WSL/Linux environments to ensure scripts are reproducible universally.

Additionally, the command invokes the Playwright skill from the forbidden .agents/skills directory. As per coding guidelines, skills must only be accessed from ~/.openclaw-autoclaw/skills/.

🔧 Proposed fix
 # 1. Start the local server (from repo root)
 python -m http.server 8765 --bind 127.0.0.1 --directory website
 
-# 2. Run the playwright baseline (separate shell)
-node "C:\Users\russe\.agents\skills\playwright\run.js" \
-  "C:\Users\russe\Documents\GitHub\PMOVES.AI\.worktrees\feat-auto-20260714-9d8a9584\pmoves\docs\evidence\website-baseline-2026-07-14\baseline.js"
+# 2. Run the playwright baseline (separate shell, from repo root)
+node "$HOME/.openclaw-autoclaw/skills/playwright/run.js" \
+  "pmoves/docs/evidence/website-baseline-2026-07-14/baseline.js"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
# 1. Start the local server (from repo root)
python -m http.server 8765 --bind 127.0.0.1 --directory website
# 2. Run the playwright baseline (separate shell)
node "C:\Users\russe\.agents\skills\playwright\run.js" \
"C:\Users\russe\Documents\GitHub\PMOVES.AI\.worktrees\feat-auto-20260714-9d8a9584\pmoves\docs\evidence\website-baseline-2026-07-14\baseline.js"
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md` around lines 151 -
158, Update the Playwright baseline command in the documented procedure to use
repository-relative or environment-independent paths instead of the hardcoded
Windows user and worktree paths, and invoke the runner from
~/.openclaw-autoclaw/skills/. Keep the local server command and baseline.js
target unchanged in purpose while ensuring the instructions work across
supported platforms.

Sources: Coding guidelines, Path instructions

Comment on lines +233 to +234
name = comp.get("component")
props = comp.get("props", {})

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Gracefully handle explicit null values in configurations.

If the parsed JSON configuration has a key explicitly set to null (e.g., "props": null), .get("props", {}) will return None, not {}. This will cause unhandled TypeErrors downstream when iterating over or checking membership in props.

Use or {} to provide a safe fallback for explicit nulls.

🐛 Proposed fix
         name = comp.get("component")
-        props = comp.get("props", {})
+        props = comp.get("props") or {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name = comp.get("component")
props = comp.get("props", {})
name = comp.get("component")
props = comp.get("props") or {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/tools/compose/compose.py` around lines 233 - 234, Update the props
assignment in the composition-processing flow to use an empty dictionary when
the configuration’s props value is missing or explicitly null. Preserve the
existing component lookup and ensure downstream iteration and membership checks
always receive a mapping.

Comment on lines +83 to +88
const linksHtml = this.links
.map((link) => {
const label = this._escapeText(link.label || link.href || 'Link');
const href = this._escapeAttr(this._safeHref(link.href));
const external = /^https?:/i.test(link.href || '');
return `<a class="link" href="${href}" ${external ? 'rel="noopener noreferrer" target="_blank"' : ''}>${label} →</a>`;

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file first, then inspect the relevant sections.
ast-grep outline pmoves/web-components/pm-project-card/pm-project-card.js --view expanded || true
echo "----"
nl -ba pmoves/web-components/pm-project-card/pm-project-card.js | sed -n '180,240p'
echo "----"
nl -ba pmoves/web-components/pm-project-card/pm-project-card.js | sed -n '70,95p'

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 1315


🌐 Web query:

WHATWG URL parser control characters scheme javascript newline tab normalization browser URL parsing

💡 Result:

The WHATWG URL Standard handles control characters, tabs, and newlines by explicitly identifying and stripping them during the URL parsing process [1]. Specifically, the basic URL parser algorithm includes steps to remove any leading or trailing C0 control characters or spaces from the input string [1]. Additionally, if the input contains any ASCII tab (U+0009) or newline (U+000A, U+000D) characters, the parser triggers an invalid-URL-unit validation error and removes all such characters from the input [1]. This behavior—stripping these characters rather than percent-encoding them—has been a point of contention and discussion within the WHATWG community [2]. While the spec currently mandates the removal of these characters, there are known differences between this specification and the practical implementation in major web browsers (such as Chrome, Firefox, and Edge), which typically percent-encode these characters (e.g., converting a newline to %0A) rather than removing them [2]. Developers have noted that the current specification's approach can cause issues for use cases like file URLs, where tabs or newlines might be significant parts of a file path [2]. Regarding hostnames and scheme-specific normalization, the URL parser is strict about forbidden characters [1][3]. There have been ongoing discussions about expanding forbidden host code points to include all C0 control characters and U+007F to prevent malicious or unexpected parsing behavior [3]. While the parser is designed to be highly standardized, these "normalization" steps (stripping vs. encoding) remain critical areas where the WHATWG specification seeks to align browser behavior while occasionally diverging from legacy expectations [1][2][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('pmoves/web-components/pm-project-card/pm-project-card.js')
lines = p.read_text().splitlines()
for start, end in [(70, 95), (200, 221)]:
    print(f'---- {start}-{end} ----')
    for i in range(start, min(end, len(lines)) + 1):
        print(f'{i:4d}: {lines[i-1]}')
PY

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 1883


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('pmoves/web-components/pm-project-card/pm-project-card.js')
text = p.read_text().splitlines()
for start, end in [(70, 95), (200, 221)]:
    print(f'---- {start}-{end} ----')
    for i in range(start, min(end, len(text)) + 1):
        print(f'{i:4d}: {text[i-1]}')
PY

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 1883


🏁 Script executed:

node - <<'JS'
const inputs = [
  'java\nscript:alert(1)',
  'java\tscript:alert(1)',
  'java\rscript:alert(1)',
  ' javascript:alert(1)',
  '/relative/path',
  '`#frag`',
  'mailto:test@example.com',
];

for (const s of inputs) {
  try {
    const u = new URL(s, 'https://example.com/base');
    console.log(JSON.stringify(s), '=>', u.href, '| protocol:', u.protocol);
  } catch (e) {
    console.log(JSON.stringify(s), '=> ERROR', e.message);
  }
}
JS

Repository: POWERFULMOVES/PMOVES.AI

Length of output: 650


Normalize href before the scheme check

_safeHref() returns the trimmed raw string for scheme-less values, so inputs like java\nscript:alert(1) bypass the regex and get normalized back to javascript: by the browser on click. Parse/normalize first, then allow only http:, https:, mailto:, and relative/fragment URLs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/web-components/pm-project-card/pm-project-card.js` around lines 83 -
88, Update the link rendering in the map callback to normalize and validate
link.href through _safeHref before determining whether it is external. Ensure
the resulting href permits only http:, https:, mailto:, and relative or fragment
URLs, rejecting obfuscated scheme-less javascript values, and perform the
external scheme check on the normalized href.

Source: Linters/SAST tools

Comment on lines +24 to +25
static get observedAttributes() {
return ['agent-name', 'agent-role', 'avatar', 'presence', 'glyph', 'theme', 'data-source'];

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Standardize the agent-role contract. The implementation renamed the property to avoid clashing with ARIA, but documentation and composed messages still use role, which the tenant renderer drops.

  • pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js#L24-L25: declare the standardized non-ARIA attribute expected by composed payloads.
  • pmoves/web-components/pm-space-agent-card/README.md#L12-L19: update the HTML example to that attribute.
  • pmoves/web-components/pm-space-agent-card/README.md#L24-L30: update JavaScript usage to agentRole.
  • pmoves/web-components/pm-space-agent-card/README.md#L35-L46: update the A2UI prop to agentRole.
  • pmoves/web-components/pm-space-agent-card/README.md#L53-L54: document agent-role, not role.
  • pmoves/web-components/README.md#L27-L35: update the registry example.
📍 Affects 3 files
  • pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js#L24-L25 (this comment)
  • pmoves/web-components/pm-space-agent-card/README.md#L12-L19
  • pmoves/web-components/pm-space-agent-card/README.md#L24-L30
  • pmoves/web-components/pm-space-agent-card/README.md#L35-L46
  • pmoves/web-components/pm-space-agent-card/README.md#L53-L54
  • pmoves/web-components/README.md#L27-L35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js` around
lines 24 - 25, Standardize the agent-role contract on agentRole/agent-role
throughout the pm-space-agent-card component and documentation. In
pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js lines 24-25,
observe agent-role; update the HTML example at
pmoves/web-components/pm-space-agent-card/README.md lines 12-19, JavaScript
usage at lines 24-30, A2UI prop at lines 35-46, and documented properties at
lines 53-54 to use agentRole or agent-role instead of role; update the registry
example in pmoves/web-components/README.md lines 27-35 accordingly.

Comment on lines +197 to +217
if (source.startsWith('http://') || source.startsWith('https://')) {
this._subscription = this._pollHttp(source);
} else if (source.includes(':')) {
this._subscription = this._subscribeNats(source);
}
}

_pollHttp(url) {
// v0.1: simple one-shot fetch. v0.2: SSE or polling with debounce.
fetch(url)
.then((r) => (r.ok ? r.json() : null))
.then((data) => {
if (data && data.presence) {
this.presence = data.presence;
}
})
.catch(() => {
// Graceful degradation: log + keep current presence value
// Per spec §13.3 v0.1: log + display fallback (no exception)
});
return { cancel: () => {} };

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cancel obsolete HTTP requests in both live components. Their cancellation handles are no-ops, so an old source can resolve after a replacement and overwrite current state.

  • pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js#L197-L217: abort or invalidate the old presence request.
  • pmoves/web-components/pm-metric-tile/pm-metric-tile.js#L208-L235: abort or invalidate the old metric request.
📍 Affects 2 files
  • pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js#L197-L217 (this comment)
  • pmoves/web-components/pm-metric-tile/pm-metric-tile.js#L208-L235
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js` around
lines 197 - 217, Update _pollHttp in
pmoves/web-components/pm-space-agent-card/pm-space-agent-card.js at lines
197-217 to make its returned cancel handle abort or invalidate the in-flight
presence request, preventing stale responses from updating presence. Apply the
same cancellation or invalidation behavior to the metric request in
pmoves/web-components/pm-metric-tile/pm-metric-tile.js at lines 208-235; both
components must prevent obsolete source requests from overwriting current state.

Comment on lines +16 to +20
connectedCallback() {
// Don't put role="list" on the host — role="list" requires listitem
// children to be DIRECT descendants, and axe-core can't see the <li>
// children inside the shadow root. The inner <ol role="list"> carries
// the proper list semantics for the rendered surface.

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add the missing role="list" to the <ol> element.

The comment explicitly mentions that "The inner <ol role="list"> carries the proper list semantics", but the role="list" attribute is missing from the actual <ol> tag in the render method. Additionally, because the <ol> has list-style: none;, WebKit/Safari will strip its list semantics unless role="list" is explicitly provided.

[accessibility]

♿ Proposed fix for the missing role attribute
-        : `<ol class="events">${eventsHtml}</ol>`}
+        : `<ol class="events" role="list">${eventsHtml}</ol>`}

Also applies to: 181-184

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/web-components/pm-timeline/pm-timeline.js` around lines 16 - 20, Add
the explicit role="list" attribute to the inner <ol> rendered by pm-timeline’s
render method, preserving the existing list structure and styles so the
shadow-root list retains accessible semantics across browsers.

Comment on lines +68 to +76
1. Create `pm-space-<your-name>/` with the standard structure:
- `<name>.js` — the Custom Element implementation
- `README.md` — API documentation
- `demo.html` — standalone demo
2. Conform to the [A2UI v0.1 spec](../contracts/a2ui-v0.1.md) — every checkbox in §11
3. Register the component in `pm-space-<your-name>/register.js`
4. Add the component to `pmoves/contracts/COMPONENT_SCHEMAS` (in `compose.py`)
5. Add the component to `SUPPORTED_COMPONENTS` (in `compose.py`)
6. Run the conformance test (`pmoves/contracts/a2ui-v0.1-conformance.test.html` in a browser)

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Register new components in the top-level registry.

Line 73 directs contributors to a per-component register.js, but these modules self-register and pmoves/web-components/register.js imports the full registry. Following this step leaves the new component unavailable to consumers.

As per path instructions, check documentation for operational accuracy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pmoves/web-components/README.md` around lines 68 - 76, Update the component
setup instructions around the per-component register.js step to direct
contributors to register new components in the top-level
pmoves/web-components/register.js registry, matching the self-registration and
full-registry import flow. Remove the inaccurate per-component registration
guidance while preserving the remaining schema, supported-component, and
conformance-test steps.

Source: Path instructions

hunnibear and others added 9 commits July 18, 2026 10:10
… image_synthesize)

Deliverables under pmoves/docs/operations/WEBSITE_UI_BASELINE_2026-07-14.md
+ pmoves/docs/evidence/website-baseline-2026-07-14/:

- 6 full-page screenshots (index+stage x desktop/tablet/mobile)
- Broken-link sweep (5 expected 404s, all founder TODOs)
- axe-core WCAG 2 AA audit: 2 real color-contrast violations
  (index.html .card-cta x10, /stage/ footer > p x1)
- 4 brand assets awaiting DARKXSIDE visual sign-off:
  og-image, favicon-512, apple-touch-icon-1024, mobile-node-rig

0 production code changes. All artifacts in docs/ + evidence/.
Lane claimed + released in AGNOTE4482PHI.t1.md.
CHIT trail signed (advisory, no CHIT_PASSPHRASE loaded).

agent_signature: ACK::Mavis-5090::WEBSITE-UI-BASELINE-RELEASE-2026-07-15
…p assignment)

Architectural direction: HTML5 Web Components (Custom Elements + Shadow DOM) as
the substrate, no framework. Lit fine for renderer layer, but the library is
plain Web Components so any agent in any language can emit them. The cf pages
get pmoves, not the other way around.

Lane contract (others will add; I shape):
1. pmoves/contracts/a2ui-v0.1.md - web-component spec
2. 5-7 component recipes (pm-space-agent-card etc.)
3. pmoves.tools.compose.compose_tenant_page() - Python callable
4. website/tenant-template/ - CF Pages tenant template
5. Fordham Hill demo - first live deployment

CHIT trail signed advisory (no CHIT_PASSPHRASE).

agent_signature: ACK::Mavis-5090::WEBSITE-AS-AGENT-CANVAS-CLAIM-2026-07-15
…trict

The contract for agent-composable UI surfaces. Defines:
- Component registry (pm- prefix, category-based naming)
- Slot composition model
- Data binding C with strict subset (push via props + single data-source, no chained pull)
- Persona theming via --pm-* CSS custom properties (15 tokens)
- ARIA rules (axe-core 4.x conformance required)
- Lifecycle (connectedCallback/disconnectedCallback cleanup guarantees)
- 7 initial component recipes (space-agent-card, project-card, metric-tile, timeline, voice-clip, image, quote-block)
- Anti-patterns (no framework, no inline styles, no global listeners, no chained pull)

Pattern credit: rooms-on-a-stage eureka from the Mavis/Opus lineage.
DARKXSIDE explicitly attributed the lineage and assigned Mavis-5090
the architectural leadership role ('other will add but you shape').

v0.1 is the lock. Breaking changes bump the major.
Components ship against this contract, not against an implementation.

Spec author: Mavis-5090
Pattern data-binding model: C (locked by DARKXSIDE 2026-07-15)
CHIT trail: ACK::Mavis-5090::A2UI-V0.1-SPEC-DRAFT::2026-07-15
…onformance

A2UI v0.1 = agent-composable web component contract. Per spec, HTML5 Web
Components (Custom Elements + Shadow DOM) as substrate, no framework. Lit
fine for renderer; the components themselves are framework-agnostic so any
agent in any language can emit them.

Deliverables:
1. pmoves/contracts/a2ui-v0.1.md - the contract (DARKXSIDE-locked C data-binding)
2. pmoves/web-components/pm-space-agent-card/ - agent identity card (v0.1)
3. pmoves/web-components/pm-project-card/ - project summary card (v0.1)
4. pmoves/web-components/pm-metric-tile/ - KPI tile w/ data-source pull (v0.1)
5. pmoves/web-components/register.js - imports all 3
6. pmoves/tools/compose/compose.py + __init__.py - compose_tenant_page()
7. pmoves/tools/compose/tests/test_compose.py - 19 tests (19/19 pass)
8. pmoves/tools/compose/tests/fixtures/fordham-hill.json - demo config
9. pmoves/contracts/a2ui-v0.1-conformance.test.html - browser conformance harness
10. pmoves/docs/evidence/website-baseline-2026-07-14/conformance-run.js - playwright runner

Conformance (all PASS):
- Registry: 3/3 v0.1 components registered
- Tokens: 50 --pm-* refs across 3 components (no hardcoded colors)
- Shadow DOM: open on all 3
- axe-core 4.10.2 (WCAG 2 AA): 0 violations, 18 rules passed

Architectural decisions (DARKXSIDE-vouched):
- role prop renamed to agentRole on pm-space-agent-card (avoids ARIA role conflict)
- ARIA attrs mirrored on HOST elements (axe-core doesn't pierce shadow DOM)
- --pm-accent reserved for backgrounds/borders, --pm-accent-soft for text on dark
- meter role gets aria-valuenow/aria-valuemin/aria-valuemax (ARIA 1.2 compliance)

Pattern credit: rooms-on-a-stage eureka from Mavis/Opus lineage.
DARKXSIDE assigned Mavis-5090 the architectural leadership role.

CHIT trail signed advisory (no CHIT_PASSPHRASE loaded).
agent_signature: ACK::Mavis-5090::A2UI-V0.1-FIRST-SLICE-SHIPPED-2026-07-15
…t template

Bundle of all 3 lanes from the WEBSITE_AS_AGENT_CANVAS super-lane:
1) 4 remaining components:
   - <pm-timeline> (event list w/ relative ts; role=region to dodge
     aria-required-children)
   - <pm-voice-clip> (native <audio controls> + collapsible transcript)
   - <pm-image> (figure w/ optional caption/credit/aspect-ratio; 7 valid
     aspect ratios)
   - <pm-quote-block> (pull-quote w/ cite; attributionRole renamed from
     role to avoid ARIA conflict)

2) Fordham Hill tenant page (the headline deliverable):
   - website/tenant-template/{index.html, tenant.css, tenant-renderer.js}
   - tenant-renderer.js fetches ./data/<tenant>.json, iterates A2UI message
     stream, creates web components, applies persona theming
   - website/tenant-template/data/fordham-hill.json (live composed artifact)
   - pmoves/tools/compose/compose_fordham_demo.py (one-off composer)

3) Compose tool wired to renderer:
   - single tool, single contract, no parallel paths
   - tenant-renderer.js reads what compose_tenant_page() produces

Conformance (all 7 v0.1 components):
  - registry: 7/7 PASS
  - tokens: 96 --pm-* refs across 7 components PASS
  - shadow DOM: open on all 7 PASS
  - axe-core 4.10.2 (WCAG 2 AA): 0 violations, 21 rules passed
  - Python unit tests: 19/19 pass

Live Fordham Hill page renders all 7 component types:
  - quote, image, 4 agent cards, 3 project cards, 4 metric tiles,
  - timeline (5 events), voice clip (native audio), final quote
  - screenshot: 3134px tall, all surfaces styled, all props applied

Architectural lessons (v0.1):
  - role attr on host conflicts with HTML role attribute
    -> rename prop (agentRole, attributionRole)
  - role='list' on host fails aria-required-children
    -> use role='region' on host, role='list' on inner <ol>
  - role='meter' on host needs aria-value* as host attrs (not shadow)

Pattern proven: the cf pages get pmoves.
Next community page is a 1-call compose + a CF Pages deploy.

CHIT trail SIGNED via make -C pmoves sign-trail (advisory, no CHIT_PASSPHRASE).
agent_signature: ACK::Mavis-5090::WEBSITE-AS-AGENT-CANVAS-PARALLEL-BUILD-2026-07-15
Four defense-in-depth gaps between the compose-tool validation and the
runtime renderer, which fetches tenant JSON without it:

- pm-image: aspect-ratio is interpolated into the shadow <style> block;
  constrain to the spec's <int>/<int> shape so it can never carry CSS/HTML
  out of that context.
- pm-project-card: _escapeAttr stops attribute breakout but not scheme
  abuse; javascript: hrefs now render as inert '#' (http/https/mailto/
  relative allowed).
- tenant-renderer applyProps: only props declared in the component's
  observedAttributes are applied — undeclared keys (innerHTML, onclick, …)
  are dropped with a console warning.
- tenant-renderer createComponent: registry components (pm-*, defined in
  customElements) only; ?tenant= values are slug-validated in both the
  renderer and index.html before becoming a fetch path.

Conformance after fix: 7/7 registered, tokens PASS, shadow DOM PASS,
axe-core 0 violations (21 rules) — identical to pre-trim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…2132)

The shipping tenant page loaded images.unsplash.com and archive.org —
website/ must self-host everything (visitor IPs were leaking to third
parties, and the stock photo was credited 'Photo: DARKXSIDE').

- pm-image: swap to ./assets/fordham-gateway.jpg — the Mavis-5090
  generated mobile-node-rig render (evidence dir), downscaled 800px/70KB,
  credited honestly as a generated render pending sign-off.
- pm-voice-clip: swap the archive.org test mp3 (labeled 2:14, actually
  ~12s of test tone) for a 4s locally generated placeholder, titled and
  timed honestly until the real clip is recorded.
- Fixed in the fixture AND regenerated via compose_fordham_demo.py so the
  composer can't resurrect the CDN refs. Zero external URLs remain.

Dev-only demo.html files under pmoves/web-components/ still use CDN
avatars; out of scope here (not website/), flagged in review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- register.js: comment claimed a try/catch made re-registration safe;
  no try/catch exists — the real guard is ES-module caching, and
  customElements.define throws on duplicates. Say so.
- index.html: <meta name="color-scheme" "dark"> was missing content=,
  parsing as a bogus boolean attribute; color-scheme never applied.
- test_compose.py: 'role' prop renamed to 'agentRole' in 4 places —
  the component deliberately renamed it to avoid clobbering the host
  ARIA role, but the tests still exercised the old name (latent drift;
  the hardened applyProps would now drop 'role' at runtime).

19/19 compose tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…TYLE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md
@POWERFULMOVES
POWERFULMOVES merged commit 854ad6e into main Jul 18, 2026
22 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the feat/a2ui-v01-fordham-hill branch July 18, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation governance AGNOTE register / agent definitions / damage-control hooks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants