The Agent Pipeline Tracker is a self-contained, browser-native HTML dashboard — no server, no Node.js proxy, no database required. Every agent record is persisted either in browser localStorage (instant, always available) or to a JSON file on SharePoint via the SP REST API (production mode for team-shared state).
The tracker was built to answer the question every MSP automation program runs into: "What agents do we have, where are they, who owns them, and when are they expected to hand off to the next stage?" That answer lives here, visible to anyone with access to the SharePoint page.
Audience: ITE Team leads, consultants, account managers, and executive stakeholders via the print report. Technicians use the Handoff column to know when new work items are landing on their plate.
Current state: Fully functional. localStorage mode works in any browser without configuration. SharePoint persistence mode requires one line of config (SP_SITE) and the target folder to exist in SharePoint. The print report generates a professional PDF-ready status report with branding, RAG status, risk dashboard, and milestone timeline.
Unlike the rest of the platform suite, the Agent Tracker requires no Node.js proxy. It uses two complementary persistence paths: localStorage for always-available fallback, and the SharePoint REST API directly from the browser for production team-shared state.
The SharePoint REST API is accessible from within a SharePoint ASPX page context because the browser is already authenticated as the logged-in user. The X-RequestDigest token is fetched from /_api/contextinfo and passed with every write operation.
nav.js is injected at the top of the file and renders the platform nav bar. The brand name override (Agent Tracker) is applied via a DOMContentLoaded listener immediately after nav.js loads, replacing the default platform brand text.
localStorage in some tenants and applies Content Security Policy restrictions. The tracker handles this gracefully: if localStorage fails it reports a storage-full warning; if the SP REST write fails it falls back to localStorage and shows an amber indicator in the top bar.
Each agent is a plain JavaScript object. The full shape is documented below. All fields except name are optional — the tracker degrades gracefully when fields are missing.
| Tag Value | Label | Special Behavior |
|---|---|---|
| copilot | Copilot | — |
| security | Security | — |
| noc | NOC | — |
| finance | Finance | — |
| hr | HR | — |
| llm | LLM | — |
| intake | Intake | — |
| reporting | Reporting | — |
| rebuild | ⚠ Rebuild | Orange checkbox accent |
| deprecated | ☽ Deprecated | Grey checkbox accent |
| tabled | ⏸ Tabled | Routes agent to Tabled view only. Hidden from all other phase filters. |
Six summary cards sit at the top of the dashboard. They are computed from the agents[] array each time renderSummary() is called — which fires on every save, delete, phase change, and initial load.
| Card | DOM ID | Value | Color |
|---|---|---|---|
| Total | s-all | agents.length | Cyan |
| Idea | s-idea | Count where phase === 'idea' | Yellow |
| Design | s-design | Count where phase === 'design' | Purple |
| Build | s-build | Count where phase === 'build' | Cyan |
| Test | s-test | Count where phase === 'test' | Orange |
| Live | s-live | Count where phase === 'live' | Green |
tabled do not increment any card counter — not even Total. This is intentional: tabled agents are parked, not active pipeline. The Total card reflects active tracked work only.
The toolbar sits between the summary cards and the main view. All controls are in a single row. Left to right: Add Agent, view toggle (Table / Board), SharePoint status dot, phase filter dropdown, search input, agent count, Export button, Print button.
| Control | Function | Notes |
|---|---|---|
| + Add Agent | Opens the new agent modal with blank fields. Phase defaults to Idea. | Keyboard: Ctrl+Enter to save while modal is open |
| ☰ Table | setView('table') — shows the sortable table view | Default view on load |
| ⊞ Board | setView('kanban') — shows the phase column board | Cards grouped by phase |
| ● SharePoint | Status indicator — shows save/load state | Green = SP saved · Yellow = SP unavailable, local cache · Blue = in-flight |
| Phase Filter | setFilter(value) — filters table and board to selected phase | Options: All Phases, Idea, Design, Build, Test, Live, ⏸ Tabled |
| Search | Live text filter on name, requestor, description, owner | oninput="renderAll()" — no debounce |
| ⬇ Export | exportJSON() — downloads agent-tracker-{date}.json | Always available, uses Blob URL |
printStatusReport() — opens pre-print dialog modal | See Print Report section | |
| ● Editing Free / ● Editing | toggleEditClaim() — claims or releases editing session | Session-scoped social lock · auto-clears on tab close · see Edit Claim Button section |
currentFilter is a module-level variable. Switching between Table and Board views preserves the active filter. When a specific phase filter is active in Board view, cards render in 2-column layout and are sorted by priority (High → Med → Low) regardless of the table sort setting.
The Edit Claim button is a lightweight session-scoped editing signal designed to mitigate the last-write-wins limitation of the shared SharePoint JSON file. It does not block saves or prevent anyone from editing — it is a social convention that tells teammates "I am actively editing right now."
The button sits in the toolbar immediately to the right of the SharePoint status dot. It uses sessionStorage — not localStorage — so the claim automatically clears the moment the browser tab is closed or the browser is quit. No stale locks. No manual cleanup required.
| State | Appearance | Meaning | sessionStorage |
|---|---|---|---|
| Unclaimed | ● Editing Free — yellow dot, subtle border | No one has claimed this session. May be safe to edit, but confirm with team if unsure. | Not set |
| Claimed | ● Editing — green glowing dot, green border | This browser tab has claimed the editing session. Team should wait or check with you before making changes. | axis_edit_claim = '1' |
sessionStorage.setItem('axis_edit_claim', '1') is set. The claim is now visible in this tab.
sessionStorage.removeItem('axis_edit_claim') is called. Signal is released for the next editor.
sessionStorage is scoped to the browser session and wiped on close by the browser itself.
sessionStorage persists across page reloads but not across tab closes. An initEditClaim() IIFE on load restores the green state if the claim is still set — so refreshing while editing keeps the signal active.
The table is rendered by renderTable() and uses filteredAgents() to apply the current filter and search. Columns are sortable. Cells are inline-editable — name, requestor, description, and owner fields are <input> elements that call updateField() on change and auto-save.
| # | Column | Editable | Sortable | Notes |
|---|---|---|---|---|
| 1 | # | No | No | Row number within current filtered set |
| 2 | Agent Name | Inline | Yes | Clickable sort · bold weight |
| 3 | Phase | Dropdown | No | Click phase badge to open inline dropdown · changing phase clears downstream est dates (regression logic) |
| 4 | Priority | Click-cycle | No | Click to cycle High → Med → Low → High |
| 5 | Requestor | Inline | No | — |
| 6 | Description | Inline | No | Full-width input · expands to content |
| 7 | Owner | Inline | No | — |
| 8 | Handoff | No | No | Computed — see Handoff Column section |
| 9 | Added | No | Yes | created field formatted as "Mar 24" |
| 10 | ✕ | No | No | Delete button — no confirmation dialog |
onchange event on an inline field calls updateField() → saveAgents(). This means a typo mid-edit may trigger a save. This is intentional — no explicit Save button is needed. If SharePoint is configured, the write fires on every field exit.
The Board view (renderKanban()) shows agents as cards grouped into 5 phase columns: Idea, Design, Build, Test, Live. Tabled agents never appear in normal board columns — they have their own dedicated view.
Each card shows: agent name + next handoff chip, requestor, description (truncated to 90 chars), tags, progressive date strip (for agents with dates set), and a footer with priority badge and added date. Clicking any card opens the edit modal.
| Filter Active | Layout | Sort | Tabled Column |
|---|---|---|---|
| All Phases | 5 columns, 1fr each | User's current sort setting | Hidden |
| Single phase (Idea–Live) | Single column, 2-col card grid | Priority: High → Med → Low | Hidden |
| ⏸ Tabled | Single tabled column | As stored | Shown — only view |
→Build Jan 23 › →Test Feb 14 › Est.Live Mar 9 › ✓ Live Mar 10. Each milestone is color-coded by type. The strip renders for all phases (Idea through Live).
The Tabled view is a parking lot for agents that are on hold — waiting for a policy decision, vendor access, budget approval, or a dependency that hasn't cleared yet. Tabled agents are invisible everywhere else in the tracker — they do not appear in any phase filter, any board column, or any summary card count.
To table an agent: open the edit modal, check the ⏸ Tabled tag. To un-table: uncheck it and re-save.
The Tabled filter is available in both Table and Board views. In Board view, a single purple-accented column appears with all tabled agents showing their original phase as a badge, so you know where they'll re-enter the pipeline when unblocked.
phase value. When you untable it, it re-appears in its original phase column and filter. Notes should explain why it was tabled — the tracker has no separate "tabled reason" field.
The modal is 820px wide and designed to fit on a single screen without scrolling. It opens via openModal() (new) or openEditModal(idx) (edit). Saved by clicking Save or pressing Ctrl+Enter. Closed by clicking Cancel, the ✕ button, or pressing Escape.
| Field | ID | Required | Notes |
|---|---|---|---|
| Agent Name | m-name | Yes | Red border flash if empty on save attempt |
| Requestor | m-req | No | Name or team |
| Owner / Builder | m-owner | No | Assigned to |
| Phase | (phase picker) | No | 5-column button grid · defaults to Idea · triggers date field enable/disable |
| Priority | m-priority | No | Select: Medium (default), High, Low |
| Description | m-desc | No | Left column of 2-col layout |
| Notes / Blockers | m-notes | No | Right column of 2-col layout |
| Est. → Build | m-est-build | No | Available from Idea onward · date input |
| Est. → Test | m-est-test | No | Available from Design onward · disabled at Idea |
| Est. → Live | m-est-live | No | Available from Build onward · disabled at Idea/Design |
| Date Went Live | m-went-live | No | Visible only when phase = Live · auto-stamped with today's date when agent first moves to Live |
| Tags | (.tag-cb checkboxes) | No | Multi-select grid · see tag table in Data Model section |
When an agent is moved backward in phase (e.g. from Build back to Design), future milestone dates are automatically cleared to avoid stale estimates. The clearing is progressive based on how far back the phase regresses:
| New Phase | Fields Cleared |
|---|---|
| Idea or Design | est_build, est_test, est_live, went_live |
| Build | est_test, est_live, went_live |
| Test | est_live, went_live |
| Live (forward) | Nothing cleared · went_live auto-stamped if not set |
saveAgent()) and the inline phase dropdown in the table (setPhase()). Both paths apply identical clearing rules.The Handoff column in the table view is designed for technicians — it answers "when is the next thing landing on my plate?" at a glance. It shows one primary date prominently and any other set dates dimmed below it.
| Agent Phase | Primary Display | Secondary (dimmed) |
|---|---|---|
| Idea | → Build date (if set) with countdown | — |
| Design | → Build date prominently with countdown | → Test date dimmed |
| Build | → Test date prominently with countdown | → Build (past), Est. Live dimmed |
| Test | Est. Live prominently with countdown | → Build, → Test (past) dimmed |
| Live | ✓ Live [date] in green | Est. was [original estimate] dimmed for accuracy review |
| No dates set | — (dash) | |
The color of the countdown chip is not a flat threshold — it adapts to the typical duration of each stage. Longer stages get more runway before turning yellow. Config lives in AT_RISK_DAYS at the top of the script block.
| Date Field | Set During Phase | Turns Yellow At | Turns Red At |
|---|---|---|---|
| est_build | Idea | 14 days | Past due |
| est_build | Design | 10 days | Past due |
| est_test | Design / Build | 14 days | Past due |
| est_live | Build | 10 days | Past due |
| est_live | Test | 7 days | Past due |
Clicking ⎙ Print opens a pre-print dialog modal that collects report metadata before generating the document. The report renders in a new window as a fully self-contained HTML document, then automatically opens the browser print dialog. The browser's "Save as PDF" option produces a properly named PDF file.
| Field | ID | Default | Notes |
|---|---|---|---|
| Organisation / Client | pr-org | MSP AI Automation Platform | Appears in cover bar and footer |
| Report Type | pr-type | Monthly Status Report | Changes content structure — see below |
| Report Period | pr-period | Current month + year | e.g. "March 2026" |
| Prepared By | pr-author | ITE Team | Appears in footer |
| View / Filter | pr-view | Current active filter label | Report covers only agents in current filtered view |
| Executive Summary / Notes | pr-summary | (auto-generated) | If blank, an auto-summary paragraph is generated from pipeline data |
| Report Type | Table Sort | Special Content | Page Target |
|---|---|---|---|
| Monthly Status Report | Phase then Priority | Full content: stats + risk dashboard + phase bar + full table | Multi-page |
| Pipeline Status Report | Phase then Priority | Adds 30-day velocity note ("X agents moved to production") | Multi-page |
| Handoff & Timeline Report | By next est. date (soonest first) | Adds sort-note callout. Table prioritizes imminent handoffs. | Multi-page |
| Executive Summary | Phase then Priority | Filtered to high-priority + overdue + blocked agents only · capped at 10 rows | 1 page target |
overdueCount × 2 + blockedCount.document.title is set to {OrgName}-Agent-Report-{YYYY-MM-DD}.pdf. When the user selects "Save as PDF" in the browser print dialog, this becomes the default file name. No jsPDF library is used — browser-native print produces reliable output.
All configuration is at the top of the script block, clearly marked. No build step, no env file — just edit the constants directly in the HTML file.
| Constant | Default | Description |
|---|---|---|
| SP_SITE | '' | The only field you must set. SharePoint site-relative path, e.g. /sites/mysite. Empty string = root site. |
| SP_DATA_FILE | agent-tracker-data.json | Filename of the JSON file written to SharePoint. Do not change unless you have multiple tracker instances. |
| SP_DATA_FOLDER | {SP_SITE}/pages/config/current | Computed from SP_SITE. The folder must exist in your SharePoint document library before first save. |
| STORAGE_KEY | axis_agent_tracker_v1 | localStorage key. Change only if you need isolated instances on the same domain. |
Risk thresholds are configured in the AT_RISK_DAYS object near the top of the script. Values represent days before the est. date when the chip turns yellow (warning). Change these to match your team's actual sprint cadences.
The tracker uses exactly three SharePoint REST API endpoints — all called from within the browser using the user's existing SharePoint authentication session. No credentials are stored in the file.
| Method | Endpoint | Purpose | Called By |
|---|---|---|---|
| POST | {origin}{SP_SITE}/_api/contextinfo | Obtain FormDigestValue (X-RequestDigest token) required for write operations |
getSPDigest() |
| GET | {origin}/_api/web/getfilebyserverrelativeurl('{SP_FILE_URL}')/$value | Read the agent JSON file from SharePoint. Returns 404 on first run (before any save). | loadFromSP() |
| POST | {origin}{SP_SITE}/_api/web/GetFolderByServerRelativeUrl('{SP_DATA_FOLDER}')/Files/Add(url='{SP_DATA_FILE}',overwrite=true) | Write (overwrite) the agent JSON file. Requires X-RequestDigest header. | saveToSP() |
| Service | URL Pattern | Purpose | Required? |
|---|---|---|---|
| Google Fonts | fonts.googleapis.com | JetBrains Mono, Exo 2, Inter fonts for the dashboard UI | No — degrades to system fonts if offline |
| QR Server | api.qrserver.com/v1/create-qr-code/?size=80x80&data={url} | Generates QR code in print reports linking back to live tracker | No — only used during report generation · broken image if offline |
Accept: application/json;odata=verbose, Content-Type: application/json;odata=verbose, and X-RequestDigest: {token}. The digest token expires after ~30 minutes — the tracker fetches a fresh one on every save, so long-running sessions are safe.
The filteredAgents() function is the single source of truth for what appears in every view. Understanding its rules prevents confusion about why agents appear or disappear.
deprecated are NOT hidden from normal filters. They appear in their phase column (typically "live" with deprecated tags) unless explicitly filtered. If you want them hidden, use the Tabled tag instead, or add a dedicated deprecated filter in a future version.
The seed data block loads only if localStorage is empty or contains data from before milestone date fields were added (i.e., no agent has est_build, est_test, or est_live). Once any agent has milestone fields, the seed never overwrites. To force a fresh seed: run localStorage.removeItem('axis_agent_tracker_v1') in the browser console and reload.
| Limitation | Impact | Workaround / Notes |
|---|---|---|
| No real-time multi-user sync | Two users saving simultaneously will overwrite each other's changes (last write wins) | Use the Edit Claim button in the toolbar as a session signal. Click to go green before editing; releases automatically on tab close. Establish team convention: see green = ask first. Also reload before editing to pull the latest SP version. |
| SP write requires folder to pre-exist | First save fails with 400 if pages/config/current/ folder doesn't exist in the SP library |
Create the folder manually in SharePoint once before first use. |
| localStorage blocked in some SP tenants | SP sandboxed pages may block localStorage writes; fallback warning shows in toolbar | Use SharePoint persistence mode (SP_SITE configured) as primary — does not depend on localStorage. |
| No attachment support | Cannot attach design docs, screenshots, or runbook links to an agent record | Use the Notes/Blockers field for SharePoint links. Consider adding a URL field in a future version. |
| No audit trail / version history | No record of who changed what or when, beyond the SharePoint file version history | SharePoint version history on the JSON file provides a basic backup. Enable versioning on the document library. |
| QR code requires internet | QR code in print reports is fetched from api.qrserver.com at report generation time | If offline, the QR image is broken but the report is otherwise complete. |
| Print table truncates long text | Description capped at 140 chars, Notes at 100 chars in printed reports | Intentional for layout consistency. Full text is visible in the tracker. |
SharePoint persistence requires one config change and one manual step in SharePoint before the first save. Once configured, saves happen automatically with every change — the same as the rest of the platform suite.
https://contoso.sharepoint.com/sites/ITPortal/Pages/tracker.aspx → SP_SITE = '/sites/ITPortal'. If the page lives at https://contoso.sharepoint.com/Pages/tracker.aspx → leave SP_SITE empty.
pages → config → current. The full path must exist: /sites/{your-site}/pages/config/current/. You only need to do this once.
agent-version-tracker.html. Find const SP_SITE = ''; near the top of the <script> block. Replace the empty string with your site-relative path (e.g. '/sites/ITPortal').
↑ Saving… in blue then ✓ Saved to SharePoint in green. If it shows yellow ⚠ SP unavailable — saved locally, the SP configuration is incorrect.
localStorage.removeItem('axis_agent_tracker_v1') then reload. This only affects your browser — other users get the SP version.
DOMContentLoaded override script runs after nav.js without error.
| Symptom | Cause | Fix |
|---|---|---|
| Yellow "SP unavailable — saved locally" on every save | SP_SITE is wrong, SP folder doesn't exist, or CORS / CSP is blocking the fetch | 1) Verify SP_SITE matches your actual site path. 2) Confirm pages/config/current/ folder exists. 3) Check browser console for 400/403/404 from the SP REST call. |
| First save succeeds but subsequent saves fail | X-RequestDigest token expired (30-min window) | The tracker fetches a fresh digest on every save — this should self-heal. If it doesn't, check if /_api/contextinfo is returning a non-200 status. |
| Data reverts to seed / old data on reload | SP load is failing and localStorage has stale data, OR seed guard is triggering | Check console for SP GET errors. If seed keeps loading, run localStorage.removeItem('axis_agent_tracker_v1') and confirm SP data file exists at the correct path. |
| Print report opens blank or errors in console | Content Security Policy on SharePoint blocks window.open() or the generated document |
SP may block popups. Allow popups for your SharePoint domain in the browser. Alternatively, the report can be refactored to open as an iframe overlay instead of a new window. |
| QR code shows broken image in PDF | No internet access when report was generated, or api.qrserver.com is blocked by network policy | Expected behavior offline. The rest of the report is unaffected. If this is a recurring issue on corporate network, remove the QR code img tag from generateReport(). |
| Tabled agents still show in All Phases board view | Agent was tagged Tabled but board view wasn't refreshed, or tag wasn't saved | Confirm the agent has "tabled" in its tags array (check via Export JSON). Trigger a manual refresh by switching views. If tags aren't saving, check for inline-edit auto-save errors in console. |
| Phase regression clears dates unexpectedly | Moving phase backward in the inline dropdown triggers setPhase() which clears downstream dates |
Expected behavior — documented in Data Model section. Use the edit modal instead of the inline dropdown if you want to move phase backward while preserving dates manually. |
| Nav bar shows "CYBERADVISORS" instead of "Agent Tracker" | nav.js brand override script isn't running, or DOMContentLoaded fired before nav.js injected the element | Confirm the override <script> block immediately follows <script src="../nav.js"></script> in the file. Check console for errors in the override block. |
| Summary cards count is wrong | Tabled agents being included, or phase mismatch | Tabled agents intentionally excluded from counts. If a non-tabled agent is missing from counts, verify its phase field matches exactly (lowercase: idea/design/build/test/live). |
| Inline edit doesn't save | SP write fails silently on onchange, or localStorage is full |
Check the toolbar status indicator immediately after editing a field. Yellow = SP fail (check console). "⚠ Storage full" = localStorage is full (rare — clear old data or switch to SP-only mode). |