You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: add ui-watchtower-setup and ui-watchtower-integration skills - #43
Adds two skills for @absaoss-cps/ngx-ui-watchtower, a telemetry library for Angular apps that sends user-journey scenarios and BI events to AWS CloudWatch RUM.
The work is split in two because the jobs differ: wiring the library is done once per app, while instrumenting journeys is repeated as the app grows.
ui-watchtower-setup installs and wires the library in an existing Angular app:
one destination chosen by the app's role: standalone app sending to RUM, micro-frontend shell, fragment, or no-op until RUM is ready;
the RUM credentials provider, an optional log provider, the user id on sign-in and sign-out, and the typed names file with the app's name prefix;
turning telemetry off in some environments, with one switch;
migration of an existing hand-written RumService.
It proves the wiring with one working, tested scenario (route-navigation tracking) and RUM page views under route templates, then hands over to the integration skill.
ui-watchtower-integration adds scenarios and BI events to an app that is already wired. It starts from the team's own questions: it asks what they want to learn, scans the code, and proposes a plan in which each item is marked as asked for (you) or suggested. It implements only what is confirmed, and tests every outcome. It can be re-run; each pass adds to what already exists.
The two skills point to each other in their NOT for: descriptions, so each one triggers only for its own job.
Key Changes
skills/ui-watchtower-setup/:
SKILL.md, a 9-step workflow;
references for micro-frontends and RUM migration;
templates for the names file, credentials provider, log provider and route-navigation service, plus specs for the last two;
10 evals, 13 trigger queries, test projects.
skills/ui-watchtower-integration/:
SKILL.md, an 8-step workflow;
instrumentation-patterns reference;
8 evals, 13 trigger queries, test projects.
skills/shared/references/ngx-ui-watchtower-api.md: the library's public API, used by both skills. Where the installed version's typings differ from the reference, the typings win. The shared skill's description is now generic.
No hardcoded versions. Setup reads the library's supported Angular range from npm at run time and installs the latest release, or the newest compatible older one (prereleases skipped). When the app's Angular is newer than the library supports, it stops; it never forces the install with --force or --legacy-peer-deps.
Naming convention.
Format: every name is kebab-case, like the library's own values (scenario-start, scenario-end, outcome reasons).
Patterns: scenarios <prefix>-<thing>-<action> (route navigation is <prefix>-route-navigate), BI events <prefix>-<thing>-<past-tense verb>, loggers <prefix>-<area>, and steps and aggregates with no prefix.
The prefix: setup asks for it and suggests the application name; integration reuses the prefix already in use.
Telemetry off per environment:withTelemetryEnabled(...) (0.2.0+) is one switch that leaves every provider in place, so wiring is identical everywhere. It's never done by leaving providers out or swapping in 'noop'. Tests keep telemetry on; the switch itself is the library's and is tested there.
Micro-frontends.
Fragments: they must use the shell's eventNamespace (asked for when the shell isn't in the repo) and the same telemetry switch.
The shell: it hands fragments a contract that states its namespace.
Channel and origin: each page load gets its own channel, published on window, and forwarding works only between same-origin realms.
BI events:
always recorded under {ns}.bi, without the per-event eventType override;
they carry sessionId and userId, like scenario records;
to keep an exact legacy type, RUM migration uses the raw sink.record.
Route-navigation template:
records each navigation when it ends, backdated to the click, so route is always the final matched template (/customers/:id), even after redirectTo, lazy routes, and guard or resolver redirects (redirectedFrom keeps the source template);
records a navigation with no end event within 30 seconds once, as timeout;
guard rejection is incomplete, supersession is abandoned, an error is failure.
No URL or personal data reaches telemetry:
navigation errors are reduced to a class-like name and an HTTP status;
unmatched URLs are recorded as (unrecognized);
RUM page views are recorded under the route template, with automatic page views turned off;
the integration skill's safeHttpFailure keeps only an HTTP status or a class-like error name, because error messages can quote URLs, ids or user data and the library only strips query strings.
Credentials provider: validates every required field of the broker's answer, rejects expired credentials, and passes the broker's other app-monitor settings through when correctly typed. Anything incomplete turns RUM off for the session (null).
RUM migration:
the cutover order guarantees the old and new RUM clients never run together, and the cutover plan names disableAutoPageView: true together with the page-view recorder;
the inventory table records exactly one parity tool per call site;
live privacy leaks in the old integration (raw search text, emails as user ids, URLs with query strings as page ids) are fixed immediately, even while the migration waits on a credentials broker.
No-op destination: the hand-over lists what switching to RUM later takes: aws-rum-web and the shimmer allowance, the RUM sink with the credentials provider, and the page-view recorder. Without the recorder, RUM records no page views.
Integration skill rules:
No wrapper: use UwtScenarioTelemetryService and UwtBITelemetryService directly where the telemetry is needed. Never merge them behind an app telemetry service or facade.
RxJS: nothing downstream of traceScenario may stop after the first value. That means no take(1) or first() after it, and no firstValueFrom() on it, because those record abandoned. Verified against the library.
Re-runs add only: existing specs stay untouched, setup included, and new tests go in their own describe.
Tested outcomes only: every outcome the proposal claims has a test. timeout is never claimed, because it's the library's safety net for a missed settle.
Docs: docs/guides/ui-watchtower-setup.md and docs/guides/ui-watchtower-integration.md (both install the shared companion), plus catalog rows in README.md and docs/README.md.
CONTRIBUTING.md: the checklist's validation command is now uvx --from skills-ref agentskills validate.
Validation
agentskills validate passes for ui-watchtower-setup, ui-watchtower-integration and shared.
Every file listed in both evals.json files exists, and no file in evals/files is unlisted.
Template type-check: the setup templates, including the route-navigation spec, and the three integration test projects type-check with 0 errors in an Angular 21.0 project against the library built from its current source. Using a name the schema doesn't declare, such as a snake_case one, fails to compile.
Templates in a real app (earlier run, before the naming change):
The two spec templates in skills/ui-watchtower-setup/assets/ (route-navigation-telemetry.service.spec.ts, rum-credentials.provider.spec.ts) passed in a real Angular 22 app with the latest published package, together with tsc -p for the app and spec tsconfigs and the SSR build.
Each behaviour was checked with a planted bug that its test caught.
The integration test projects contain the route-navigation template unchanged except for the scenario name, adapted to each app's prefix.
Trigger evals: 13 queries per skill, 3 runs each, run with the other skill installed as a competitor. Both skills scored 13/13: every should-trigger query triggered 3/3 and every should-not query 0/3. No query for one skill triggered the other.
Skill evals: all 18 evals were run 3 times with the skill and 3 times without.
Each run was graded strictly against the expectations in evals.json, with evidence (file and line) for every verdict.
The runs could not install, build or test, so a pass means the files and the report satisfy the expectation.
Skill / eval
Expectations
With skill (3 runs)
Without skill (mean)
setup / standalone app
14
14 · 14 · 14
33%
setup / SSR shell hosting fragments
7
7 · 7 · 7
71%
setup / fragment
7
7 · 7 · 7
71%
setup / RUM not ready (noop)
7
7 · 7 · 7
57%
setup / telemetry off in dev
7
7 · 7 · 7
57%
setup / partly wired app
6
6 · 6 · 6
50%
setup / existing RumService migration
8
8 · 8 · 8
13%
setup / no barrel re-export of /rum
3
3 · 3 · 3
78%
setup / unsupported Angular version
6
6 · 6 · 6
83%
setup / non-Angular app
3
3 · 3 · 3
100%
integration / vague request asks first
7
7 · 7 · 7
57%
integration / user-supplied requirements
6
6 · 6 · 6
44%
integration / conflicting request
5
5 · 5 · 5
93%
integration / settle status and RxJS
7
7 · 7 · 7
57%
integration / re-run adds only
7
7 · 7 · 7
71%
integration / missing precondition
3
3 · 3 · 3
44%
integration / no merged telemetry wrapper
6
6 · 6 · 6
33%
integration / user list still gets code research
6
6 · 6 · 6
17%
Totals:
setup: 100% with the skill (204/204) vs 54% without (111/204);
integration: 100% (141/141) vs 52% (74/141).
Without the skill, agents typically:
left the RUM migration mostly undone;
used snake_case or unprefixed BI names;
didn't label proposal items by source;
settled an empty result as success, and passed the raw HttpErrorResponse (which quotes the URL) to fail();
tested through a noop sink instead of a recording one;
with setup missing, built their own performance.measure timing instead of stopping and pointing to setup.
This observable example only constructs the pipeline and then discards it; because RxJS observables are lazy, no request runs and the scenario is never settled as shown. Make the example return the traced observable from a method or subscribe to it so it demonstrates an actually executable integration.
Use the detected package manager for both installs
skills/ui-watchtower-setup/SKILL.md:115
Step 1 says to use the detected package manager for every install, but the only commands shown here are npm install. An agent following this section for a Yarn or pnpm project can create a second lockfile and bypass the repository's package-manager workflow. Show the equivalent command for the detected manager (or use an explicit package-manager placeholder) for both installs.
🧠 Review effort: Lite
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
The backdating assertion advances a real wall clock by 60 ms and accepts any result above 50 ms. This makes the test depend on timing/scheduler behavior and adds avoidable delay; use a fake/injected clock and advance it deterministically (also for the identical test at line 275).
Use a fake clock for deterministic backdating assertions
The backdating assertion advances a real wall clock by 60 ms and accepts any result above 50 ms. This makes the test depend on timing/scheduler behavior and adds avoidable delay; use a fake/injected clock and advance it deterministically (also for the identical test at line 275).
Preserve all supported app-monitor configuration fields
The broker response is described as the app-monitor config, but this reconstruction drops every supported optional field except sessionSampleRate (for example telemetries, sessionEventLimit, allowCookies, sessionAttributes, or pagesToInclude). A valid broker response using any of those settings will silently run with different RUM behavior; the migration guide also relies on carrying session attributes through this config. Preserve the validated optional config fields while still forcing disableAutoPageView: true, or explicitly validate/map the complete contract.
Correct route-navigation test count in validation summary
The PR description reports this route-navigation spec as both 19 cases (Overview) and 11 cases (Validation), but the current spec contains 20 it(...) cases. Please correct the validation summary so the claimed test coverage matches the committed fixture.
🧠 Review effort: Lite
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
fateeand
changed the title
feat: Add skills: ngx-ui-watchtower setup and integration
feat: add ui-watchtower-setup and ui-watchtower-integration skills
Oct 8, 2026
@copilot resolve the merge conflicts in this pull request
Merged master and resolved the shared companion conflict in merge commit 6462082. The Watchtower API reference remains available; upstream’s unrelated legacy living-documentation files were removed.
The reason will be displayed to describe this comment to others. Learn more.
isTelemetry lets any [name, {...}] through. The options come from JSON, but aws-rum-web wants RegExps/functions in some of them (urlsToExclude, ignore...). For example, ["http", {"urlsToExclude": ["/health"]}] makes the patched fetch/XHR throw test is not a function on every request. I'd drop the options object or only allow a few known JSON-safe keys.
The reason will be displayed to describe this comment to others. Learn more.
window.top only works if the shell is the top-level page. If the shell app is itself embedded as an iframe in another app, fragments silently fall back to an unhosted channel. Maybe walk up window.parent until we find __uwtTelemetryChannel, with a try/catch per level? The check in Verifying (L150) assumes the same thing.
The reason will be displayed to describe this comment to others. Learn more.
One keepalive fetch per record with no batching. A burst over 64 KiB gets rejected and the .catch hides it, so logs are lost silently. The comment mentions batching, but SKILL.md tells people to copy this as-is. Either batch and send in flush(), or use keepalive only in flush().
The reason will be displayed to describe this comment to others. Learn more.
With no router we remove disableAutoPageView, but the spec copied in Step 7 still asserts it in two tests, so they go red. Worth adding a note to update those expectations.
The reason will be displayed to describe this comment to others. Learn more.
nit: "specs for the last two" means log-api + route-navigation, but the specs that ship are for rum-credentials + route-navigation. There's no log-api spec, so just name both.
The reason will be displayed to describe this comment to others. Learn more.
nit: this docstring has drifted from the asset. A throw at startup is retried (every 30 s, up to 10 times), so it doesn't just leave RUM off. Same in wired-checkout / wired-instrumented and in the comment in app.config.ts L38-39.
"prompt": "Set up ngx-ui-watchtower in this Angular app. RUM isn't ready yet — we don't have a credentials endpoint. No log backend. The project is in evals/files/standalone-angular.",
"expected_output": "Skill wires provideUwtTelemetry with the explicit 'noop' destination, installs only the library (no aws-rum-web, no shimmer allowance, no credentials provider), still adds the schema, route-navigation proof and tests, and explains the one-line swap to the RUM sink later.",
The reason will be displayed to describe this comment to others. Learn more.
nit: "one-line swap to the RUM sink" contradicts Step 9 ("it is more than one line") and the expectation on L114.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Adds two skills for
@absaoss-cps/ngx-ui-watchtower, a telemetry library for Angular apps that sends user-journey scenarios and BI events to AWS CloudWatch RUM.The work is split in two because the jobs differ: wiring the library is done once per app, while instrumenting journeys is repeated as the app grows.
ui-watchtower-setupinstalls and wires the library in an existing Angular app:RumService.It proves the wiring with one working, tested scenario (route-navigation tracking) and RUM page views under route templates, then hands over to the integration skill.
ui-watchtower-integrationadds scenarios and BI events to an app that is already wired. It starts from the team's own questions: it asks what they want to learn, scans the code, and proposes a plan in which each item is marked as asked for (you) or suggested. It implements only what is confirmed, and tests every outcome. It can be re-run; each pass adds to what already exists.The two skills point to each other in their
NOT for:descriptions, so each one triggers only for its own job.Key Changes
skills/ui-watchtower-setup/:SKILL.md, a 9-step workflow;skills/ui-watchtower-integration/:SKILL.md, an 8-step workflow;skills/shared/references/ngx-ui-watchtower-api.md: the library's public API, used by both skills. Where the installed version's typings differ from the reference, the typings win. Thesharedskill's description is now generic.--forceor--legacy-peer-deps.scenario-start,scenario-end, outcome reasons).<prefix>-<thing>-<action>(route navigation is<prefix>-route-navigate), BI events<prefix>-<thing>-<past-tense verb>, loggers<prefix>-<area>, and steps and aggregates with no prefix.applicationname; integration reuses the prefix already in use.withTelemetryEnabled(...)(0.2.0+) is one switch that leaves every provider in place, so wiring is identical everywhere. It's never done by leaving providers out or swapping in'noop'. Tests keep telemetry on; the switch itself is the library's and is tested there.eventNamespace(asked for when the shell isn't in the repo) and the same telemetry switch.window, and forwarding works only between same-origin realms.{ns}.bi, without the per-eventeventTypeoverride;sessionIdanduserId, like scenario records;sink.record.routeis always the final matched template (/customers/:id), even afterredirectTo, lazy routes, and guard or resolver redirects (redirectedFromkeeps the source template);timeout;incomplete, supersession isabandoned, an error isfailure.(unrecognized);safeHttpFailurekeeps only an HTTP status or a class-like error name, because error messages can quote URLs, ids or user data and the library only strips query strings.null).disableAutoPageView: truetogether with the page-view recorder;aws-rum-weband theshimmerallowance, the RUM sink with the credentials provider, and the page-view recorder. Without the recorder, RUM records no page views.UwtScenarioTelemetryServiceandUwtBITelemetryServicedirectly where the telemetry is needed. Never merge them behind an app telemetry service or facade.traceScenariomay stop after the first value. That means notake(1)orfirst()after it, and nofirstValueFrom()on it, because those recordabandoned. Verified against the library.describe.timeoutis never claimed, because it's the library's safety net for a missed settle.docs/guides/ui-watchtower-setup.mdanddocs/guides/ui-watchtower-integration.md(both install thesharedcompanion), plus catalog rows inREADME.mdanddocs/README.md.CONTRIBUTING.md: the checklist's validation command is nowuvx --from skills-ref agentskills validate.Validation
agentskills validatepasses forui-watchtower-setup,ui-watchtower-integrationandshared.Every file listed in both
evals.jsonfiles exists, and no file inevals/filesis unlisted.Template type-check: the setup templates, including the route-navigation spec, and the three integration test projects type-check with 0 errors in an Angular 21.0 project against the library built from its current source. Using a name the schema doesn't declare, such as a snake_case one, fails to compile.
Templates in a real app (earlier run, before the naming change):
skills/ui-watchtower-setup/assets/(route-navigation-telemetry.service.spec.ts,rum-credentials.provider.spec.ts) passed in a real Angular 22 app with the latest published package, together withtsc -pfor the app and spec tsconfigs and the SSR build.Trigger evals: 13 queries per skill, 3 runs each, run with the other skill installed as a competitor. Both skills scored 13/13: every should-trigger query triggered 3/3 and every should-not query 0/3. No query for one skill triggered the other.
Skill evals: all 18 evals were run 3 times with the skill and 3 times without.
evals.json, with evidence (file and line) for every verdict./rumTotals:
Without the skill, agents typically:
HttpErrorResponse(which quotes the URL) tofail();performance.measuretiming instead of stopping and pointing to setup.Closes #42