From e8eae4c27a3b8df533f6dee4b5b6f4c96e94c747 Mon Sep 17 00:00:00 2001 From: Andrew Farah <7025farah@gmail.com> Date: Wed, 24 Jun 2026 21:22:51 -0700 Subject: [PATCH] Add portable Density guidance source --- README.md | 12 +- plugins/density/guidance/design.md | 89 ++++++ .../density/guidance/skills/benchmarking.md | 50 ++++ .../density/guidance/skills/data-health.md | 63 ++++ plugins/density/guidance/skills/density.md | 268 ++++++++++++++++++ plugins/density/guidance/skills/floorplan.md | 45 +++ .../density/guidance/skills/sensor-health.md | 39 +++ plugins/density/guidance/skills/setup.md | 93 ++++++ .../density/guidance/skills/utilization.md | 56 ++++ plugins/density/guidance/skills/wayfinding.md | 68 +++++ plugins/density/skills/data-health/SKILL.md | 9 +- plugins/density/skills/density/SKILL.md | 16 +- plugins/density/skills/setup/SKILL.md | 28 +- plugins/density/skills/wayfinding/SKILL.md | 18 ++ .../density/test/plugin-packaging.test.mjs | 30 ++ 15 files changed, 868 insertions(+), 16 deletions(-) create mode 100644 plugins/density/guidance/design.md create mode 100644 plugins/density/guidance/skills/benchmarking.md create mode 100644 plugins/density/guidance/skills/data-health.md create mode 100644 plugins/density/guidance/skills/density.md create mode 100644 plugins/density/guidance/skills/floorplan.md create mode 100644 plugins/density/guidance/skills/sensor-health.md create mode 100644 plugins/density/guidance/skills/setup.md create mode 100644 plugins/density/guidance/skills/utilization.md create mode 100644 plugins/density/guidance/skills/wayfinding.md diff --git a/README.md b/README.md index 7ffcced..7916d03 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,17 @@ The first managed runtime asset is published for `darwin-arm64`. Other platforms The default setup path prepares a fast starter preload. For broader customer-owned history, use `historical_export`; the plugin should not treat the starter preload as a limit on local data access. -The plugin ships one visual design contract at `plugins/density/assets/design.md`. Edit that file when a customer-specific artifact style is needed. +## Guidance Source + +The plugin keeps portable product guidance in `plugins/density/guidance/`. + +Codex still loads the packaged skill files from `plugins/density/skills/*/SKILL.md` and the visual contract from `plugins/density/assets/design.md`. Keep those packaged files mirrored from the shared guidance source; the packaging tests fail when they drift. + +Use this split to keep Codex polished while leaving the guidance easy to package for other hosts later. Platform-specific files such as `skills/*/agents/openai.yaml` stay in the Codex skill folders. + +The plugin ships one visual design contract at `plugins/density/assets/design.md`, mirrored from `plugins/density/guidance/design.md`. Edit the guidance source and packaged asset together when a customer-specific artifact style is needed. + +After changing skill text or plugin packaging, reinstall/update the plugin and start a new Codex thread so the latest skills load. CLI/runtime-only changes can be tested immediately when the managed runtime is pointed at a local dev CLI. ## Marketplace Layout diff --git a/plugins/density/guidance/design.md b/plugins/density/guidance/design.md new file mode 100644 index 0000000..432fe38 --- /dev/null +++ b/plugins/density/guidance/design.md @@ -0,0 +1,89 @@ +# Density Artifact Design + +Use this file as the single visual contract for Density plugin artifacts. If a user wants customer-specific branding, edit this file instead of adding a second competing style guide. + +## Principles + +- The default aesthetic is Broadsheet/Tufte: concise analytical brief, high signal-to-ink ratio, direct claims, restrained rules, and no generic dashboard chrome. +- Lead with the answer, then show the evidence. +- Make every important number comparative. Show the measured value with the denominator or baseline that makes it interpretable, then add the nearest internal comparison and a named Density benchmark when available. +- Define analytical terms where the reader sees them. Put working day, business hours, local time, utilization, time used, saturation, and availability definitions in subtitles, labels, legends, or notes instead of relying on chat context. +- Prefer calm analytical briefs over generic dashboards. +- Make floorplans, charts, tables, caveats, and provenance feel like one system. +- Keep visuals useful when exported, screenshotted, or read without the chat context. +- Never hide data-quality caveats behind decorative design. + +## Default Look + +- Background: warm off-white or white. +- Text: dark neutral, high contrast. +- Accent: Density rust `#8c2f1d` for the primary finding. +- Secondary colors: restrained neutrals, muted teal, and muted blue for comparisons or availability states. +- Typography: large serif titles for report pages; compact sans-serif labels and table text for dense analytical surfaces. Chart titles should read like a sentence-case claim, not a dashboard widget label. +- Radius: 8px or less for cards, controls, and repeated items. +- Avoid decorative gradients, floating blobs, heavy shadows, and marketing-style hero layouts. +- Avoid chart-card styling when the artifact itself is the answer. The chart should feel like a page or brief, not a screenshot of a dashboard tile. + +## Charts + +- Show a visible source badge: `Local`, `Live`, `Benchmark`, or `Mixed`. +- `Local` means customer-owned historical data from local Parquet/DuckDB. +- `Live` means current availability or presence from the authenticated live feed. +- `Benchmark` means display-safe Density benchmark-network context. +- `Mixed` means the artifact combines more than one source layer; label each part clearly. +- Use direct labels when practical. +- Use legends only when direct labels would clutter the chart. +- Legends, badges, subtitles, and labels must never overlap the title, marks, axes, or each other. Prefer direct labels or a below-chart legend when the title is long. +- Sort ranked charts by the metric being discussed. +- Show units in labels or subtitles, such as hours per day, person-hours, percent of working hours, or spaces. +- Avoid naked stats in titles, labels, and callouts. Prefer patterns like "5.8% of the working day (8am-6pm local time), +1.9 pts vs building baseline" or "3.1 hours per room per day, 0.7 above portfolio average." +- Use the accent color only for the lead series or important highlight. +- Keep comparison series muted. +- Include the time window, business-hours assumption with definition, timezone basis, and data freshness when relevant. +- Keep chart generation dependency-light but not layout-blind. If rendering by hand, reserve explicit title, subtitle, legend, plot, and footnote regions before drawing marks. +- Prefer generated chart artifacts from the Density CLI or plugin chart contract over ad hoc one-off scripts. If a one-off fallback is unavoidable, it must still follow this design file and be visually inspected for collisions. + +## Atlas Analytics Defaults + +- Treat Atlas as the baseline product grammar for local analytics. +- Show effective scope when it affects interpretation: org, building/floor, timezone, date window, operating hours, working days, and interval. +- Default utilization charts to local Atlas operating hours, typically `8am-6pm`, unless the artifact says otherwise. +- Use local timezone projections from Atlas-style views for hour, weekday, heatmap, and working-hours displays. +- Never label a UTC-grouped chart as local business-hour analysis. +- Prefer top/bottom 12 ranked bars for room, booth, and capacity findings. +- Use gray for no data, a separate neutral for zero observed use, and an explicit caveat for low uptime or unhealthy signals. +- For saturation/runout visuals, write the threshold in the subtitle or legend. + +## Floorplans + +- Preserve the user's ability to read the base floorplan. +- Use overlays for the analytical signal, not for decoration. +- Use a clear status palette: + - available: muted green or teal + - occupied or most-used: Density rust + - unavailable, stale, or unhealthy: muted gray + - missing data: light neutral with explicit label or legend entry +- For historical utilization, use rank, intensity, or labels rather than live availability wording. +- For real-time wayfinding, use current availability wording and avoid implying a historical trend. + +## Tables And Lists + +- Put ranked findings in a compact table or list near the visualization. +- Include space name, space type, capacity when known, metric value, comparison value, delta or ratio, and data-quality note when needed. +- Keep numbers rounded enough to scan, but not so rounded that rankings become misleading. + +## Caveats And Provenance + +Every analytical artifact should make these visible when they matter: + +- customer or org scope +- building, floor, or space scope +- date range +- business-hours window, including timezone and weekday/all-day basis +- local data freshness +- whether the answer used local Parquet/DuckDB, live APIs, or benchmark APIs +- missing, stale, or filtered data + +## Modification Contract + +Users may modify this file to match customer branding. Keep the file name and role stable so all Density skills continue to share one visual source of truth. diff --git a/plugins/density/guidance/skills/benchmarking.md b/plugins/density/guidance/skills/benchmarking.md new file mode 100644 index 0000000..67a0606 --- /dev/null +++ b/plugins/density/guidance/skills/benchmarking.md @@ -0,0 +1,50 @@ +--- +name: benchmarking +description: Use when the user asks how a Density customer, building, floor, room type, workplace pattern, or utilization result compares against peer benchmarks or target ranges. +--- + +# Density Benchmarking + +Use this skill for benchmark scorecards, peer comparisons, target ranges, and portfolio-level interpretation. + +Always use `../../assets/design.md` for visual artifacts. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. +- Define operational terms in place when they affect the comparison, especially working day, business hours, local time, utilization, time used, saturation, and availability. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +Read `references/darshan-benchmark-methodology.md` before answering benchmark math, scorecard, peer comparison, or recommendation questions. + +## Rules + +- Do not invent benchmark thresholds. +- Use `benchmark_compare` when available. +- Prefer Darshan's benchmark scorecard methodology where available. +- Treat every benchmark answer as comparative by construction: measured customer value, nearest internal customer comparison, then the approved Density benchmark segment when available. +- Name the benchmark segment specifically, such as room-size bucket, space function, floor type, capacity bucket, region, or workplace cohort returned by the benchmark source. Avoid generic phrases like "broader benchmark" when a specific segment is available. +- Use numbers and percentages together when they improve comprehension: measured value, denominator or baseline, delta, ratio, percentile, sample size, and reliability. +- Include the benchmark time basis when available, such as "per working day (8am-6pm local time)" or "during business hours (defined by the building settings)." If the benchmark source does not expose the basis, say that the time-basis comparison is unavailable. +- Keep raw peer distributions server-side or inside the benchmark API contract. +- Return display-safe benchmark findings, not raw customer peer rows. +- Separate findings from recommendations. +- State when sample size or data quality makes a benchmark unreliable. +- If no approved benchmark source is connected, say that benchmark context is unavailable instead of deriving peer context from local customer Parquet. +- Never describe Density benchmark context as another customer's data. It is generalized, approved comparative intelligence only. + +## Data Routing + +Use local Parquet/DuckDB for the customer's historical metrics. Use benchmark APIs or approved benchmark snapshots for peer context. diff --git a/plugins/density/guidance/skills/data-health.md b/plugins/density/guidance/skills/data-health.md new file mode 100644 index 0000000..34dacfc --- /dev/null +++ b/plugins/density/guidance/skills/data-health.md @@ -0,0 +1,63 @@ +--- +name: data-health +description: Use when the user asks why Density local data is missing, stale, zero, inconsistent with Atlas, too slow, or not suitable for local Parquet/DuckDB analytics. +--- + +# Density Data Health + +Use this skill for local Parquet/DuckDB readiness, freshness, zero-data diagnosis, sync gaps, and repair guidance. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +Prefer the plugin MCP tools when available: + +- `setup` +- `local_data_profile` +- `data_health_report` +- `storage_report` +- `starter_questions` +- `repair_fast_questions` +- `onboard_customer` +- `historical_export` + +## Diagnosis Checklist + +Check these before answering an analytical question from local data: + +- canonical Parquet tables exist +- normalized fast-question tables exist +- local metrics cover the requested time window +- timestamps overlap the user's requested date range +- space metadata joins to metrics +- space type filters match the product taxonomy +- parent/child hierarchy is handled correctly +- uptime or health filters are not removing everything +- starter answers include nonzero useful results when applicable + +## Response Rule + +When data is not good enough, say exactly what is missing and what evidence showed that. Then give one primary next action: repair metadata, sync metrics, export Parquet, warm starter questions, or narrow the question. + +If the issue is that the requested window is broader than the recent preload, check onboarding/background-history status when available. If a background deeper-history job is still running, say the local dataset is recent-first and still filling in deeper history. If no job exists, recommend the deeper-history onboarding/export path rather than implying the local-first product is capped at the preload window. + +## Recovery Rules + +Treat sync state as diagnostic evidence, not as user-facing detail by default. +When debugging local data freshness or sync gaps, use `state.json` as the source of truth for cursors, `updatedSince`, `lastSyncAt`, and rows synced. +Prefer deterministic recovery: retry with the same cursor first, and rebuild only as an explicit fallback. +Use incomplete recent data only for deliberate diagnostics; do not quietly use it for normal utilization answers. diff --git a/plugins/density/guidance/skills/density.md b/plugins/density/guidance/skills/density.md new file mode 100644 index 0000000..cda56d7 --- /dev/null +++ b/plugins/density/guidance/skills/density.md @@ -0,0 +1,268 @@ +--- +name: density +description: Use Density as the parent router for local-first workplace analytics, setup, floorplans, wayfinding, utilization, benchmarking, sensor health, data health, and visual artifacts. +--- + +# Density + +Use this skill whenever the user wants to install, set up, inspect, sync, demo, or ask questions of Density data. + +The user should interact with Density first. The plugin may use the Density CLI internally, but do not make the user memorize CLI setup details unless something is genuinely blocked. + +## Interaction Contract + +Answer like a concise workplace and buildings expert: + +- Lead with the practical answer, then add only the source, freshness, and caveat needed to trust it. +- Treat understanding as comparative. Do not present an important stat by itself. Pair it with the denominator, capacity, baseline, previous period, peer floor, building average, portfolio average, or another known comparison that makes the number interpretable. +- When useful and available, use this comparison ladder: measured value, nearest internal customer comparison, then a named Density benchmark segment from `benchmark_compare` or an approved benchmark source. Do not imply a benchmark is another customer's data; Density benchmarks must be generalized and display-safe. +- Define operational terms in place the first time they affect interpretation. For example, write "working day (8am-6pm local time, weekdays)" or "business hours (9am-5pm in the building timezone)" instead of assuming the user knows the window. +- Keep tool mechanics out of user-facing prose unless the user asks how it works, an action is blocked, or the exact command/tool name changes what they should do next. +- For ambiguous scope, ask one crisp clarifying question before querying: building, floor, space type, time window, or whether they mean current availability versus historical utilization. +- Keep live/current truth separate from historical/local truth. Use live sources for now/open/available questions; use local historical data for trends, busiest spaces, exports, and charts. +- Treat building status/go-live readiness as part of scope, not as a footnote. Before choosing a broad building or answering about a named building, use `available_buildings` when available and carry status, go-live state, metric coverage, geometry, and eligibility into the response. +- Prefer human names for buildings, floors, spaces, and labels. Avoid raw org, location, space, sensor, or UUID-style ids unless the user asks, debugging requires them, or two similarly named things must be disambiguated. +- Stay professional, direct, and lightly human. Do not oversell certainty, bury the answer in caveats, or sound like a CLI manual. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +## Skill Routing + +Use the sibling skills as expert homes, but keep the product hierarchy clear: + +- Local foundation: `setup` and `data-health`. +- Activation layer: `utilization` and `floorplan`. +- Gated moat surfaces: `benchmarking` and `wayfinding`. +- Trust layer: `sensor-health`. + +If an answer includes a chart, HTML report, table, or floorplan, use `../../assets/design.md` as the only visual contract. +For broad prompts such as "pick any building" or "compare any one site," prefer the `answer_density_question` front door or a bounded `local_data_profile`/data-health check before asking the user to wait. If the tool returns `kind: density.clarification_request.v1` with the `density.clarification` contract, ask one crisp clarification using its `suggestions` and `freeform` fields, then resume with `nextActionAfterAnswer`. Do not fall back to shell, DuckDB, SQL, or hand-built Parquet scans for ordinary questions unless the user asks for debugging or the plugin tools are genuinely unavailable. + +## Data Boundary Contract + +- `local_customer_data`: customer-owned historical data in local Parquet/DuckDB. Use this for utilization, exports, charts, private analysis, and first-value activation. Do not meter access to this data by default. +- `benchmark_network_context`: Density-owned comparative intelligence. Use only through `benchmark_compare`, approved benchmark APIs, or approved snapshots. Never expose peer rows, peer org ids, raw distributions, or histogram buckets. +- `live_feed`: current-state availability and presence. Use `live_wayfinding_status` or live wayfinding sources for now/open/available questions. Historical local data may be offered as context, never as live truth. + +Every analytical answer should disclose the source layer, time window, freshness, confidence, and caveat when those affect interpretation. Name exact tools or commands only when requested, blocked, or needed for trust. +Every important metric should be grounded with a known comparison. Prefer numeric context over vague language: use values, percentages, and deltas such as "5.8% of the working day (8am-6pm local time), 1.9 points above the building baseline of 3.9%" instead of qualitative labels alone. If no trustworthy comparison exists, say that comparison context is unavailable rather than inventing one. + +## Building Lifecycle Contract + +Use `available_buildings` before analysis when the user asks for a named building, asks for "any" building/site/office, asks for current availability, or asks for an artifact that implies a building can be charted or mapped. +Querying a planning, inactive, retired, future, or unknown-go-live building is allowed, but the answer and any artifact must say so plainly. +Do not imply a building is live just because it exists in local metadata. +For historical charts, prefer buildings where `chartQueryable` is true; otherwise explain which status/go-live readiness or metric-coverage field blocks the artifact. +For live wayfinding, require `liveWayfindingEligible`; historical utilization can be context only, not a walkable recommendation. + +## Core Workflow + +Prefer the plugin MCP tools when available: + +- `setup` +- `auth_login` +- `onboard_customer` +- `historical_export` +- `create_demo_customer` +- `ask_chart` +- `local_utilization_query` +- `floor_usage_report` +- `local_data_profile` +- `available_buildings` +- `data_health_report` +- `repair_fast_questions` +- `live_wayfinding_status` +- `benchmark_compare` +- `sensor_health_report` +- `storage_report` +- `starter_questions` + +Use the scripts below from the plugin root as the fallback when the MCP tools are not loaded in the current session. + +1. Run setup/doctor first when the user is new, unsure, or asking about onboarding: + +```bash +node scripts/density-setup.mjs --json +``` + +If setup returns `update.available: true`, tell the user: + +```text +A newer version of the Density plugin is available. Say `update @density` and I can install it. +``` + +Only run the returned update command after the user says yes, `update @density`, `update density`, or an equivalent explicit approval. After updating, ask the user to start a new thread so the latest Density skill and tools load. + +2. If setup says local data is missing, use `onboard_customer` or the fallback script. Present onboarding as recent-first local data, not as a cap on customer-owned history. Recommend fetching 30 days for all locations now and continuing deeper supported history in the background when that capability is available. Offer recent-only or a named building/floor/location slice when the scoped onboarding resolver is available. + +```bash +node scripts/density-onboard-customer.mjs --json +``` + +Use explicit full sync when the user is ready to fetch the recent local dataset: + +```bash +node scripts/density-onboard-customer.mjs --full-sync --days=30 --json +``` + +The default recent preload is 30 days. Windows up to 7 days use 15-minute metrics; longer windows use hourly metrics so setup stays practical locally. +Explicit full sync prewarms starter-question answers and SVG/HTML chart artifacts when the CLI supports it. When background deeper-history sync is supported, the recommended 30-day full sync should continue the full supported local history window in the background. Pass `prewarmQuestions: false` only when the user wants raw sync/export without the fast-answer cache. + +For a separate broader local history export, use `historical_export`: + +```text +historical_export +``` + +The default historical export window is 90 days and the maximum supported local history window is 365 days. This is still customer-owned local data; benchmark context and live availability remain separate source layers. + +3. If the user needs demo data from an existing local customer dataset, create a fresh Parquet-first local data dir: + +```bash +node scripts/density-demo-customer.mjs \ + --source=/path/to/existing/.density-cli \ + --out=/path/to/demo/.density-cli \ + --days=14 \ + --json +``` + +This produces canonical `parquet/*.parquet` files plus a small DuckDB catalog of views over Parquet. That is the preferred demo/onboarding shape. Avoid copying or preserving a large hydrated DuckDB file as durable storage. +For fast utilization questions, the demo/onboarding output must include the normalized question inputs as well as generic bulk tables: `spaces`, `space_labels`, `space_children`, and `space_metrics`. If setup reports `fastQuestionsReady: false`, follow its `onboard_customer` action before treating chart answers as useful. + +4. Ask a local historical utilization question and render an inline chart when setup reports chart support: + +```bash +node scripts/density-ask-chart.mjs \ + --data-dir=/path/to/demo/.density-cli \ + --question="what are the busiest rooms?" \ + --json +``` + +For a spatial floorplan artifact, use `floor_usage_report` instead of `ask_chart`. The fallback command is: + +```bash +density viz --html --report floor-usage --format json +``` + +Then answer in Codex with: + +- title +- subtitle +- inline image using the absolute `png` path +- optional artifact paths for SVG and HTML + +Example Markdown: + +```markdown +![Density chart](/absolute/path/to/chart.png) +``` + +If `ask_chart` reports unsupported chart capability, say plainly that this CLI/plugin pair does not support chart questions yet and offer the returned next action or `density viz --html` fallback. + +For MCP sessions, prefer `local_utilization_query` over `ask_chart` for historical utilization. A basic historical question should normally be one MCP tool call; do not run setup/profile first unless the answer reports missing, stale, or unsupported local data. Keep `ask_chart` as a compatibility path. +For chart follow-ups such as "can I see a chart?", reuse the prior chart context or call the plugin chart artifact path first. Do not write a custom chart script unless the plugin reports chart artifacts are unsupported; if a fallback script is required, it must follow `../../assets/design.md`. + +5. When checking whether local data can support a fast interactive discussion, use `starter_questions`. On newer CLIs it runs the built-in 100-question pack through `density question --starter --chart --format json` and returns timings plus SVG/HTML artifacts and an artifact manifest path. After a warmup, pass `cached: true` to reopen that manifest quickly. For starter questions and equivalent phrasing, `ask_chart` tries the warmed manifest first through `density question --cached --chart --format ui`, then falls back to live local querying on a cache miss. On older CLIs it returns static suggested questions and an update action. + +## Setup Principles + +- Keep the setup path short: + 1. Install/enable Density. + 2. Ask Codex: `Set up Density`. + 3. Follow the one primary next action only when setup cannot continue safely. + 4. Ask a question. +- Prefer browser auth. If auth fails, tell the user exactly that the next step is `density auth login`. +- Use `DENSITY_CLI_DATA_DIR` or the script `--data-dir` flag for customer-specific local data. +- The plugin should find the CLI through `DENSITY_CLI_COMMAND`, `DENSITY_CLI_BIN`, `DENSITY_CLI_REPO`, `density` on PATH, or known local development checkouts. +- Prefer explicitly configured or repo-local CLIs over PATH discovery, and report CLI provenance in setup output. +- Do not ask the user to memorize CLI commands unless setup is blocked. + +## Storage Rule + +Parquet is the durable local store. DuckDB is the query engine and, ideally, a small working catalog/cache. + +For customer-scale local data: + +- Good: `parquet/*.parquet` is present and DuckDB is small or disposable. +- Good: DuckDB contains views over Parquet for query-only/demo data. +- Suspicious: DuckDB is many times larger than Parquet and treated as the durable artifact. + +When reporting storage, include DuckDB and Parquet sizes, expected Parquet table readiness, and a plain next action. Treat DuckDB and Parquet as sensitive local customer data: avoid printing row contents or unnecessary absolute data paths. +For fast question answering, also check `fastQuestionsReady`; generic `parquetReady` alone can still be insufficient if the normalized space metadata is missing. + +## Artifact Design Rule + +Use `../../assets/design.md` for charts, HTML reports, tables, floorplans, and visual explanations. Do not introduce a second customer-specific style file by default. If the user wants customer-specific styling, edit or override that one design file. + +When Codex needs inline display, convert SVG to PNG with `rsvg-convert` and embed the PNG. + +## Atlas Defaults Rule + +Local historical analytics should behave like a fast Atlas extension: + +- Use effective scope returned by the CLI when present. +- Use space, floor, or building timezone from local metadata; UTC is only a fallback caveat. +- Use Atlas local projections such as `atlas_local_metrics.day_id`, `weekday`, `hour`, and `time_zone` for day, hour, working-hours, and heatmap answers. +- Do not group raw UTC metric timestamps for local business-hour or weekday analysis. +- Default local utilization charts to Atlas operating hours, typically `8am-6pm`, unless the user asks for a different window. +- Apply weekday/business-day semantics when the user asks for business, working, or weekday usage. +- Prefer `15minute` rows for short windows and `hour` rows for longer windows when the CLI reports that choice. + +## First Value Loop + +The first-run product loop is: + +1. Set up local customer data. +2. Answer one useful historical utilization question locally and fast. +3. Show what Density benchmark-network context or live feed would add when relevant. +4. Track background deeper-history syncs when supported, and use `historical_export` when the user needs an explicit separate local history export. +5. Use `data-health` or `sensor-health` when trust in the answer is uncertain. + +## Good Local Test Questions + +Known fast paths: + +```text +what are the busiest rooms? +what are the least used rooms? +what time are rooms busiest? +what are the busiest phone booths? +what are the busiest rooms and phone booths? +``` + +Generated related specs: + +```text +which room capacities are used most? +which room capacities are used most on weekends? +show me a pie chart of space type breakdown +what kinds of spaces are represented? +``` + +Filtered variants: + +```text +what are the busiest rooms in the morning? +what are the busiest rooms in the afternoon? +when are rooms busiest on weekdays? +``` + +## Response Shape + +For chart answers, use this shape: + +```markdown +Title sentence. +Subtitle sentence. + +![Short chart alt](/absolute/path/to/chart.png) +``` + +Keep implementation details out of the first answer unless the user asks how it works. diff --git a/plugins/density/guidance/skills/floorplan.md b/plugins/density/guidance/skills/floorplan.md new file mode 100644 index 0000000..a6c1d6d --- /dev/null +++ b/plugins/density/guidance/skills/floorplan.md @@ -0,0 +1,45 @@ +--- +name: floorplan +description: Use when the user wants Density spaces, utilization, rankings, health, or live availability shown visually on a floorplan. +--- + +# Density Floorplan + +Use this skill for Density floorplan artifacts and overlays. + +Always use `../../assets/design.md` as the visual contract. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. +- Define operational terms in subtitles, legends, or callouts when they affect interpretation, especially working day, business hours, local time, utilization, saturation, and availability. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +## Rules + +- Preserve the floorplan as the spatial reference. +- Keep overlays semantically accurate: live availability, historical utilization, benchmark status, and sensor health are different signals. +- Use other spaces as light context when the user asks to focus on a subset. +- Label only what helps interpretation; avoid cluttering every polygon. +- Include a legend, scope, time window, and data-source note. +- If an overlay uses working-day, business-hours, saturation, or utilization language, define it in the legend or subtitle. + +## Data Routing + +- Historical utilization floorplan artifacts should use `floor_usage_report` when MCP tools are available, or `density viz --html --report floor-usage --format json` as the fallback. +- Historical utilization overlays should use the `utilization` skill. +- Real-time availability overlays should use the `wayfinding` skill. +- Sensor coverage or offline/stale overlays should use the `sensor-health` skill. +- Missing local data or zero-data diagnosis should use the `data-health` skill. diff --git a/plugins/density/guidance/skills/sensor-health.md b/plugins/density/guidance/skills/sensor-health.md new file mode 100644 index 0000000..a8a5ef6 --- /dev/null +++ b/plugins/density/guidance/skills/sensor-health.md @@ -0,0 +1,39 @@ +--- +name: sensor-health +description: Use when the user asks about Density sensor health, stale or missing live signals, offline spaces, data coverage, uptime, or why availability/utilization data may be unreliable. +--- + +# Density Sensor Health + +Use this skill for cloud sensor health, live signal trust, coverage, uptime, stale data, and operational data-quality explanations. + +Always use `../../assets/design.md` for visual artifacts. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +Read `references/sensor-health-methodology.md` when interpreting health status or explaining what unhealthy data means. + +## Rules + +- Use `sensor_health_report` when available. +- Sensor health is cloud-only. Do not infer it from DuckDB, Parquet, local historical utilization, or zero/nonzero occupancy rows. +- Separate product health from workplace behavior. +- Do not call a space unused when the sensor or data path is unhealthy. +- For live wayfinding, stale or unhealthy signals should appear as unknown or unavailable, not confidently available. +- For historical utilization, low uptime should be filtered or flagged. +- Explain what is missing, what evidence shows it, and what action would repair it. diff --git a/plugins/density/guidance/skills/setup.md b/plugins/density/guidance/skills/setup.md new file mode 100644 index 0000000..3e5fe37 --- /dev/null +++ b/plugins/density/guidance/skills/setup.md @@ -0,0 +1,93 @@ +--- +name: setup +description: Use when the user wants to install, authenticate, check readiness, sync, repair, or prepare local Density data for fast Parquet-first analytics. +--- + +# Density Setup + +Use this skill for Density installation, auth, setup checks, local data preparation, storage reports, and repair flows. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +Prefer the plugin MCP tools when available: + +- `setup` +- `install_managed_cli` +- `auth_login` +- `onboard_customer` +- `historical_export` +- `create_demo_customer` +- `storage_report` +- `available_buildings` +- `starter_questions` +- `repair_fast_questions` + +Fallback scripts live in the plugin root under `scripts/`. + +## Workflow + +1. Run setup or `node scripts/density-setup.mjs --json`. +2. If setup says a plugin update is available, tell the user: "A newer version of the Density plugin is available. Say `update @density` and I can install it." Run the returned update command only after the user says yes, `update @density`, `update density`, or an equivalent explicit approval. After updating, ask the user to start a new thread so the latest Density skill and tools load. +3. If setup asks for the managed CLI runtime, use `install_managed_cli`. This is an explicit download/copy action that verifies the manifest checksum before installing into `~/.density-cli/plugin-runtime/`. +4. If auth is missing, use `auth_login` or tell the user the next step is browser auth. +5. If Parquet or fast-question inputs are missing, present the onboarding choices from setup/onboard_customer. Recommend fetching 30 days for all locations now and continuing deeper supported history in the background when that capability is available. +6. If generic Parquet exists but normalized fast-question metadata is missing and repair is available, use `repair_fast_questions`. +7. Confirm lifecycle readiness is advertised. If setup reports that building lifecycle/go-live readiness is missing, update the CLI before trusting building-level analysis artifacts. +8. Use `available_buildings` when the user asks which buildings are available, live, queryable, mapped, or eligible for wayfinding. +9. Use `storage_report` when the user asks what is local, stale, oversized, or suspicious. +10. Use onboarding status when available to check a background deeper-history job and tell the user when the full supported local history is ready. Use `historical_export` when the user explicitly asks for a separate broader customer-owned local history export. + +Normal setup should not run `npm install` or build the CLI from source. Use `DENSITY_CLI_REPO` plus `DENSITY_CLI_BUILD_FROM_SOURCE=1` only for explicit development work. + +## Local Storage Contract + +Parquet is durable. DuckDB is the query engine and cache. + +Good local analytics stores include canonical Parquet tables plus normalized fast-question inputs such as: + +- `spaces` +- `space_labels` +- `space_children` +- `space_metrics` +- `space_occupancy` + +Treat `parquetReady` as necessary but not sufficient for utilization. For fast historical questions, also check `fastQuestionsReady` and starter-cache usefulness when available. + +## Onboarding Choices + +When setup reaches local data preparation, present these choices: + +- Recommended: fetch 30 days for all locations now, then continue the remaining supported history in the background when supported. +- Recent only: fetch 30 days for all locations and skip the background history job. +- Specific location: fetch a named building, floor, or location slice when the CLI scoped onboarding resolver is available. + +Windows up to 7 days may use 15-minute metrics; longer windows use hourly metrics to keep setup practical. Background deeper-history sync uses the CLI historical export path, which splits Data Access API observation requests at UTC calendar-month boundaries. + +Do not describe the recent preload as a limit on customer access to their own data. Until background history completes, disclose that local history is recent-first and still filling in deeper history. + +## Wayfinding Readiness + +Setup should also prepare live wayfinding without pulling historical utilization just for wayfinding: + +- spaces and labels +- building/floor hierarchy +- floorplans and geometry +- identifiers needed by the real-time availability hook + +Do not ask users to set up API tokens for wayfinding. Live wayfinding should use the stored browser-auth Atlas session and the user's Density permissions. +Do not call latest synced data "live" unless the command used a true live availability hook. diff --git a/plugins/density/guidance/skills/utilization.md b/plugins/density/guidance/skills/utilization.md new file mode 100644 index 0000000..0b9ba8a --- /dev/null +++ b/plugins/density/guidance/skills/utilization.md @@ -0,0 +1,56 @@ +--- +name: utilization +description: Use when the user asks historical Density utilization questions, busiest spaces, least-used spaces, meeting-room or phone-booth usage, working-hours averages, or local Parquet/DuckDB analytics. +--- + +# Density Utilization + +Use this skill for historical utilization, occupancy, time-used, and ranking questions. + +Always prefer local Parquet/DuckDB data first. Use live APIs only when the user asks for real-time/current availability. + +Always use `../../assets/design.md` for visual artifacts. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Do not give standalone utilization numbers. Pair occupied hours, percent utilized, time used, saturation, or rankings with a denominator or comparison such as capacity, working-hours window, prior period, floor average, building average, portfolio average, or another known internal baseline. +- Prefer numeric context over qualitative shorthand: say "5.8% of the working day (8am-6pm local time), 1.9 points above the building baseline of 3.9%" rather than only saying "higher pressure." +- Define operational terms in place the first time they matter. Prefer "working day (8am-6pm local time, weekdays)" over "working day" and "time used (share of intervals with occupancy above zero)" over "time used" when that definition affects interpretation. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Check building lifecycle before making building-level claims. If `available_buildings` is available, use it for named buildings and broad "any building/site" prompts before presenting analysis or artifacts. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +For metric definitions and query rules, read `references/atlas-utilization-methodology.md` when the answer depends on math, normalization, rollups, or data-quality interpretation. + +## Workflow + +1. Use `local_utilization_query` directly for normal historical questions. It should carry effective scope, freshness, confidence, and caveats. +2. Check local readiness with `setup`, `storage_report`, or `data-health` only when the query result says data is missing, stale, all zero, or unsupported. +3. For named or broad building scope, use `available_buildings` and prefer buildings with `chartQueryable: true` for artifacts. If a building is planning, retired, inactive, future go-live, or unknown go-live, query only with that caveat visible in the response. +4. If you must query manually, use Atlas local views and the effective scope rules in `references/atlas-utilization-methodology.md`. +5. Sync or repair only when that is the right next action for the user's request. +6. Report the source layer, tool, date range, business-hours assumption with definition, freshness, confidence, and caveats. +7. When relevant, add the nearest internal comparison first, then use Density benchmark-network context through `benchmark_compare` if benchmark access is available. +8. For broad scope prompts such as "any one building," use the plugin front door or data-profile coverage plus lifecycle readiness to choose a valid measured scope. If the local question router says the scope is missing, do not turn that into a long manual DuckDB/Parquet investigation in the user-facing answer; either recover through the plugin surfaces or ask one crisp clarification. +9. For chart follow-ups, reuse a prior chart artifact or use the plugin chart path before creating any fallback script. + +## Default Assumptions + +- Working-day analyses should state the business-hours window used in the same sentence or parenthetical, such as "working day (8am-6pm local time, weekdays)." +- Default Atlas-style utilization charts to the CLI-reported effective scope, usually `8am-6pm` local time. +- Use working days when the user asks for business, working, or weekday usage; otherwise disclose whether all days or weekdays were used. +- If the user gives no window, use the prepared local data window and disclose it. +- If the user says "last two weeks", use 14 days if available. +- For room and booth rankings, prefer time-used or occupied-hours metrics over raw event counts. diff --git a/plugins/density/guidance/skills/wayfinding.md b/plugins/density/guidance/skills/wayfinding.md new file mode 100644 index 0000000..c0f45d3 --- /dev/null +++ b/plugins/density/guidance/skills/wayfinding.md @@ -0,0 +1,68 @@ +--- +name: wayfinding +description: Use when the user wants real-time Density availability, live occupancy, open rooms, desks, phone booths, or navigable wayfinding on a floorplan. +--- + +# Density Wayfinding + +Use this skill for live or real-time availability. Do not use historical utilization tables to answer live wayfinding questions. + +Always use `../../assets/design.md` for visual artifacts. + +## Interaction Contract + +- Lead with the practical workplace answer, then the source, freshness, confidence, and caveat needed to trust it. +- Keep CLI, MCP, shell, cache, and tool-routing mechanics out of user-facing prose unless the user asks, an action is blocked, or those mechanics change the next step. +- Ask one crisp clarifying question when building, floor, space type, time window, or current-versus-historical scope is ambiguous. +- Keep local historical data, live availability, benchmark context, and sensor health separate. +- Prefer human-readable names and labels. Avoid raw ids unless the user asks or debugging requires them. + +## Progress Update Contract + +Keep user-visible progress updates at the workplace level: + +- Say what decision you are making for the user, not which skill, MCP tool, CLI command, cache path, SQL query, or local file is being used. +- Do not mention parser misses, reserved SQL words, DuckDB internals, shell commands, skill loading, or tool routing unless the user explicitly asks for debugging. +- If a query misroutes or needs a retry, recover quietly and disclose only the resulting source, scope, freshness, confidence, or caveat needed to trust the final answer. +- Good updates sound like: "I am checking the local historical window and office scope" or "I am using complete local business days (weekdays within the stated local working-hours window) so a partial day does not understate utilization." + +## Rules + +- Treat "available now", "open", "occupied", "live", "real-time", and "wayfinding" as current-state questions. +- Use `live_wayfinding_status` or live availability/presence data when available. +- Before making a walkable recommendation for a building, use `available_buildings` when available and require `liveWayfindingEligible: true`. +- The live wayfinding source is floor presence, such as `v3/{orgId}/analytics/ws/floor/{floorId}/presence`, when the CLI or app can access it. +- Clearly separate live status from historical popularity. +- If a live source is unavailable, say that plainly and offer the closest historical alternative as a fallback, not as a replacement. +- When the user asks for only one space type, hide other types or show them only as faint spatial context. +- If the building is planning, inactive, retired, future go-live, or unknown go-live, do not present it as live wayfinding-ready. +- Do not ask users to create or paste API tokens for wayfinding. Live wayfinding should use browser-auth/Atlas session permissions. +- Always say that availability means occupancy availability, not calendar booking status. +- If route geometry or floorplan highlighting is missing, do not promise directions. Return the available room or matching space information instead. + +## Live Floorplans + +For floor-level availability or desk availability, prefer the dynamic wayfinding floorplan artifact when the CLI/tool returns one. +The artifact must be live-updating, not a static snapshot. +It should render cached floorplan geometry once, then let the page own current presence state. + +When handling live floorplan state: + +- Keep a browser-side `presenceBySpace` map keyed by space id. +- Apply both `refresh` and `live` socket messages into that map. +- Ignore out-of-order updates when an incoming timestamp is older than the current value for a space. +- Re-render affected floorplan polygons and counts after each message. +- Reconnect after socket close or error; do not freeze the page after the first refresh. +- Show floor local time and the observed timestamp range so all-available/all-occupied displays can be interpreted against office hours and signal freshness. + +## Floorplan Labels + +Use current-state language: + +- available +- occupied +- unavailable +- unknown +- stale or unhealthy when the live signal is not trustworthy + +If a live response includes health state, treat `healthy` as trustworthy, and treat `offline`, `unknown`, or `degraded` as a reason to avoid confident availability claims. diff --git a/plugins/density/skills/data-health/SKILL.md b/plugins/density/skills/data-health/SKILL.md index 4e2c0af..34dacfc 100644 --- a/plugins/density/skills/data-health/SKILL.md +++ b/plugins/density/skills/data-health/SKILL.md @@ -53,4 +53,11 @@ Check these before answering an analytical question from local data: When data is not good enough, say exactly what is missing and what evidence showed that. Then give one primary next action: repair metadata, sync metrics, export Parquet, warm starter questions, or narrow the question. -If the issue is that the requested window is broader than the starter preload, recommend `historical_export` rather than implying the local-first product is capped at the preload window. +If the issue is that the requested window is broader than the recent preload, check onboarding/background-history status when available. If a background deeper-history job is still running, say the local dataset is recent-first and still filling in deeper history. If no job exists, recommend the deeper-history onboarding/export path rather than implying the local-first product is capped at the preload window. + +## Recovery Rules + +Treat sync state as diagnostic evidence, not as user-facing detail by default. +When debugging local data freshness or sync gaps, use `state.json` as the source of truth for cursors, `updatedSince`, `lastSyncAt`, and rows synced. +Prefer deterministic recovery: retry with the same cursor first, and rebuild only as an explicit fallback. +Use incomplete recent data only for deliberate diagnostics; do not quietly use it for normal utilization answers. diff --git a/plugins/density/skills/density/SKILL.md b/plugins/density/skills/density/SKILL.md index 03a5c67..cda56d7 100644 --- a/plugins/density/skills/density/SKILL.md +++ b/plugins/density/skills/density/SKILL.md @@ -100,28 +100,28 @@ A newer version of the Density plugin is available. Say `update @density` and I Only run the returned update command after the user says yes, `update @density`, `update density`, or an equivalent explicit approval. After updating, ask the user to start a new thread so the latest Density skill and tools load. -2. If setup says local data is missing, use `onboard_customer` or the fallback script. This is a starter preload for fast first value, not a cap on customer-owned local history. The default path is staged: it may sync cheap metadata, then returns one primary next action for longer starter metrics/export work instead of hiding a long all-spaces sync. +2. If setup says local data is missing, use `onboard_customer` or the fallback script. Present onboarding as recent-first local data, not as a cap on customer-owned history. Recommend fetching 30 days for all locations now and continuing deeper supported history in the background when that capability is available. Offer recent-only or a named building/floor/location slice when the scoped onboarding resolver is available. ```bash node scripts/density-onboard-customer.mjs --json ``` -Use explicit full sync only when the user is ready for longer local work: +Use explicit full sync when the user is ready to fetch the recent local dataset: ```bash -node scripts/density-onboard-customer.mjs --full-sync --days=14 --json +node scripts/density-onboard-customer.mjs --full-sync --days=30 --json ``` -The default starter metrics preload is 14 days. Windows up to 7 days use 15-minute metrics; longer windows use hourly metrics so two-week utilization questions stay practical locally. -Explicit full sync prewarms starter-question answers and SVG/HTML chart artifacts when the CLI supports it. Pass `prewarmQuestions: false` only when the user wants raw sync/export without the fast-answer cache. +The default recent preload is 30 days. Windows up to 7 days use 15-minute metrics; longer windows use hourly metrics so setup stays practical locally. +Explicit full sync prewarms starter-question answers and SVG/HTML chart artifacts when the CLI supports it. When background deeper-history sync is supported, the recommended 30-day full sync should continue the full supported local history window in the background. Pass `prewarmQuestions: false` only when the user wants raw sync/export without the fast-answer cache. -For broader local history, use `historical_export` instead of stretching onboarding: +For a separate broader local history export, use `historical_export`: ```text historical_export ``` -The default historical export window is 90 days and the maximum is 365 days. This is still customer-owned local data; benchmark context and live availability remain separate source layers. +The default historical export window is 90 days and the maximum supported local history window is 365 days. This is still customer-owned local data; benchmark context and live availability remain separate source layers. 3. If the user needs demo data from an existing local customer dataset, create a fresh Parquet-first local data dir: @@ -222,7 +222,7 @@ The first-run product loop is: 1. Set up local customer data. 2. Answer one useful historical utilization question locally and fast. 3. Show what Density benchmark-network context or live feed would add when relevant. -4. Use `historical_export` when the user needs more local history than the starter preload. +4. Track background deeper-history syncs when supported, and use `historical_export` when the user needs an explicit separate local history export. 5. Use `data-health` or `sensor-health` when trust in the answer is uncertain. ## Good Local Test Questions diff --git a/plugins/density/skills/setup/SKILL.md b/plugins/density/skills/setup/SKILL.md index 2a4b925..3e5fe37 100644 --- a/plugins/density/skills/setup/SKILL.md +++ b/plugins/density/skills/setup/SKILL.md @@ -45,12 +45,12 @@ Fallback scripts live in the plugin root under `scripts/`. 2. If setup says a plugin update is available, tell the user: "A newer version of the Density plugin is available. Say `update @density` and I can install it." Run the returned update command only after the user says yes, `update @density`, `update density`, or an equivalent explicit approval. After updating, ask the user to start a new thread so the latest Density skill and tools load. 3. If setup asks for the managed CLI runtime, use `install_managed_cli`. This is an explicit download/copy action that verifies the manifest checksum before installing into `~/.density-cli/plugin-runtime/`. 4. If auth is missing, use `auth_login` or tell the user the next step is browser auth. -5. If Parquet or fast-question inputs are missing, use `onboard_customer` for the starter preload. +5. If Parquet or fast-question inputs are missing, present the onboarding choices from setup/onboard_customer. Recommend fetching 30 days for all locations now and continuing deeper supported history in the background when that capability is available. 6. If generic Parquet exists but normalized fast-question metadata is missing and repair is available, use `repair_fast_questions`. 7. Confirm lifecycle readiness is advertised. If setup reports that building lifecycle/go-live readiness is missing, update the CLI before trusting building-level analysis artifacts. 8. Use `available_buildings` when the user asks which buildings are available, live, queryable, mapped, or eligible for wayfinding. 9. Use `storage_report` when the user asks what is local, stale, oversized, or suspicious. -10. Use `historical_export` when the user wants broader customer-owned local history beyond the starter preload. +10. Use onboarding status when available to check a background deeper-history job and tell the user when the full supported local history is ready. Use `historical_export` when the user explicitly asks for a separate broader customer-owned local history export. Normal setup should not run `npm install` or build the CLI from source. Use `DENSITY_CLI_REPO` plus `DENSITY_CLI_BUILD_FROM_SOURCE=1` only for explicit development work. @@ -68,10 +68,26 @@ Good local analytics stores include canonical Parquet tables plus normalized fas Treat `parquetReady` as necessary but not sufficient for utilization. For fast historical questions, also check `fastQuestionsReady` and starter-cache usefulness when available. -## Sync Defaults +## Onboarding Choices -The default starter metrics preload is 14 days. Windows up to 7 days may use 15-minute metrics; longer windows may use hourly metrics to keep two-week answers practical. +When setup reaches local data preparation, present these choices: -Prefer staged setup unless the user explicitly wants a longer full sync. +- Recommended: fetch 30 days for all locations now, then continue the remaining supported history in the background when supported. +- Recent only: fetch 30 days for all locations and skip the background history job. +- Specific location: fetch a named building, floor, or location slice when the CLI scoped onboarding resolver is available. -For larger local history, use `historical_export`. Do not describe the starter preload limit as a limit on customer access to their own data. +Windows up to 7 days may use 15-minute metrics; longer windows use hourly metrics to keep setup practical. Background deeper-history sync uses the CLI historical export path, which splits Data Access API observation requests at UTC calendar-month boundaries. + +Do not describe the recent preload as a limit on customer access to their own data. Until background history completes, disclose that local history is recent-first and still filling in deeper history. + +## Wayfinding Readiness + +Setup should also prepare live wayfinding without pulling historical utilization just for wayfinding: + +- spaces and labels +- building/floor hierarchy +- floorplans and geometry +- identifiers needed by the real-time availability hook + +Do not ask users to set up API tokens for wayfinding. Live wayfinding should use the stored browser-auth Atlas session and the user's Density permissions. +Do not call latest synced data "live" unless the command used a true live availability hook. diff --git a/plugins/density/skills/wayfinding/SKILL.md b/plugins/density/skills/wayfinding/SKILL.md index 0502a66..c0f45d3 100644 --- a/plugins/density/skills/wayfinding/SKILL.md +++ b/plugins/density/skills/wayfinding/SKILL.md @@ -36,6 +36,24 @@ Keep user-visible progress updates at the workplace level: - If a live source is unavailable, say that plainly and offer the closest historical alternative as a fallback, not as a replacement. - When the user asks for only one space type, hide other types or show them only as faint spatial context. - If the building is planning, inactive, retired, future go-live, or unknown go-live, do not present it as live wayfinding-ready. +- Do not ask users to create or paste API tokens for wayfinding. Live wayfinding should use browser-auth/Atlas session permissions. +- Always say that availability means occupancy availability, not calendar booking status. +- If route geometry or floorplan highlighting is missing, do not promise directions. Return the available room or matching space information instead. + +## Live Floorplans + +For floor-level availability or desk availability, prefer the dynamic wayfinding floorplan artifact when the CLI/tool returns one. +The artifact must be live-updating, not a static snapshot. +It should render cached floorplan geometry once, then let the page own current presence state. + +When handling live floorplan state: + +- Keep a browser-side `presenceBySpace` map keyed by space id. +- Apply both `refresh` and `live` socket messages into that map. +- Ignore out-of-order updates when an incoming timestamp is older than the current value for a space. +- Re-render affected floorplan polygons and counts after each message. +- Reconnect after socket close or error; do not freeze the page after the first refresh. +- Show floor local time and the observed timestamp range so all-available/all-occupied displays can be interpreted against office hours and signal freshness. ## Floorplan Labels diff --git a/plugins/density/test/plugin-packaging.test.mjs b/plugins/density/test/plugin-packaging.test.mjs index 4e5ff80..aacbc3a 100644 --- a/plugins/density/test/plugin-packaging.test.mjs +++ b/plugins/density/test/plugin-packaging.test.mjs @@ -12,6 +12,7 @@ const testDir = path.dirname(fileURLToPath(import.meta.url)); const pluginRoot = path.resolve(testDir, '..'); const repoRoot = path.resolve(pluginRoot, '..', '..'); const skillsDir = path.join(pluginRoot, 'skills'); +const guidanceDir = path.join(pluginRoot, 'guidance'); const EXPECTED_SKILLS = [ 'benchmarking', @@ -79,8 +80,18 @@ test('Density skills have valid packaging metadata and shared contracts', async } }); +test('Density packaged skills mirror shared guidance source', async () => { + for (const skillName of EXPECTED_SKILLS) { + const packaged = await readFile(path.join(skillsDir, skillName, 'SKILL.md'), 'utf8'); + const guidance = await readFile(path.join(guidanceDir, 'skills', `${skillName}.md`), 'utf8'); + + assert.equal(packaged, guidance, `${skillName} SKILL.md is stale relative to shared guidance`); + } +}); + test('Density package files are tracked by git', async () => { const pathspecs = [ + 'plugins/density/guidance', 'plugins/density/skills', 'plugins/density/assets', 'plugins/density/scripts', @@ -100,6 +111,9 @@ test('Density package files are tracked by git', async () => { test('Density design contract preserves Broadsheet/Tufte chart requirements', async () => { const design = await readFile(path.join(pluginRoot, 'assets', 'design.md'), 'utf8'); + const guidance = await readFile(path.join(guidanceDir, 'design.md'), 'utf8'); + + assert.equal(design, guidance, 'packaged design contract is stale relative to shared guidance'); assert.match(design, /Broadsheet\/Tufte/, 'design contract should name the intended analytical aesthetic'); assert.match(design, /high signal-to-ink ratio/, 'design contract should retain the Tufte-style signal discipline'); @@ -122,6 +136,22 @@ test('Density skills preserve building lifecycle and go-live analysis rules', as assert.match(wayfinding, /liveWayfindingEligible/, 'wayfinding skill should require live wayfinding eligibility'); }); +test('Density guidance preserves portable setup, data-health, and live-wayfinding rules', async () => { + const density = await readFile(path.join(skillsDir, 'density', 'SKILL.md'), 'utf8'); + const setup = await readFile(path.join(skillsDir, 'setup', 'SKILL.md'), 'utf8'); + const dataHealth = await readFile(path.join(skillsDir, 'data-health', 'SKILL.md'), 'utf8'); + const wayfinding = await readFile(path.join(skillsDir, 'wayfinding', 'SKILL.md'), 'utf8'); + + assert.match(density, /30 days/, 'parent skill should describe recent-first 30-day onboarding'); + assert.match(setup, /recent-first and still filling in deeper history/, 'setup should preserve background history disclosure'); + assert.doesNotMatch(setup, /- `onboarding_status`/, 'setup should not advertise unavailable onboarding_status MCP tool'); + assert.match(dataHealth, /state\.json/, 'data-health should preserve CLI sync-state diagnostics'); + assert.match(dataHealth, /retry with the same cursor first/, 'data-health should prefer deterministic cursor recovery'); + assert.match(wayfinding, /presenceBySpace/, 'wayfinding should preserve live floorplan state map guidance'); + assert.match(wayfinding, /Apply both `refresh` and `live` socket messages/, 'wayfinding should preserve socket message handling'); + assert.match(wayfinding, /observed timestamp range/, 'wayfinding should preserve signal freshness display guidance'); +}); + test('Density MCP server version and tool list match the plugin package', async () => { const manifest = JSON.parse(await readFile(path.join(pluginRoot, '.codex-plugin', 'plugin.json'), 'utf8')); assert.equal(manifest.website, 'https://density.io/');