fsanyoto commited on
Commit
85ee8e5
Β·
verified Β·
1 Parent(s): 9b857f7

Deploy AIOS web (React glide grid + FastAPI slice)

Browse files
RELEASES.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
- "current": "91ad73a",
3
  "releases": [
4
  {
5
  "version": "v25",
 
1
  {
2
+ "current": "6d8d6ae",
3
  "releases": [
4
  {
5
  "version": "v25",
VERSION CHANGED
@@ -1 +1 @@
1
- 91ad73a
 
1
+ 6d8d6ae
api/routes_alerts.py CHANGED
@@ -193,7 +193,11 @@ def notification_view(item):
193
  if kind == NOTIF_KIND_SHARE:
194
  sender = actor or "A teammate"
195
  elif kind == NOTIF_KIND_AUTOMATION:
196
- sender = actor or "Automation"
 
 
 
 
197
  else:
198
  sender = actor or "Alerts"
199
  out = {**item, "read": bool(item.get("read")), "kind": kind,
 
193
  if kind == NOTIF_KIND_SHARE:
194
  sender = actor or "A teammate"
195
  elif kind == NOTIF_KIND_AUTOMATION:
196
+ # β›” "Agents", not "Automation" (W34-T40, corrected at QA 2026-08-17). The client's
197
+ # `senderOf` already falls back to `AGENTS_MODULE_LABEL` β€” but `if (sent) return sent`
198
+ # runs FIRST, so this server literal won and every actor-less automation notification
199
+ # showed the retired module name in the inbox's From column.
200
+ sender = actor or "Agents"
201
  else:
202
  sender = actor or "Alerts"
203
  out = {**item, "read": bool(item.get("read")), "kind": kind,
web/src/assistant/AssistantPage.tsx CHANGED
@@ -54,12 +54,16 @@ const isMode = (value: unknown): value is AssistantMode => value === "chat" || v
54
  * B's to own. The arrival parameter is consumed once, below, and stored.
55
  * Pure and exported so the decision can be checked without a browser.
56
  */
57
- export function initialMode(hash: string, stored: string | null): AssistantMode {
58
  const mark = hash.indexOf("?");
59
- if (mark >= 0) {
60
- const asked = new URLSearchParams(hash.slice(mark + 1)).get("mode");
61
- if (isMode(asked)) return asked;
62
- }
 
 
 
 
63
  return isMode(stored) ? stored : "chat";
64
  }
65
 
@@ -352,14 +356,28 @@ export default function AssistantPage({ granted }: AssistantPageProps) {
352
  * by following a `#/query` link, never touching the toggle, would have the parameter stripped
353
  * here and then land back in Chat on the next reload, restored from a choice made days earlier.
354
  * Following the link IS choosing.
 
 
 
 
 
 
 
 
 
355
  */
356
  useEffect(() => {
357
- if (typeof window === "undefined" || !window.location.hash.includes("?")) return;
358
- storeMode(mode);
359
- window.history.replaceState(null, "", `#/${ASSISTANT_ROUTE}`);
360
- // `mode` is read once, at mount, on purpose: this effect exists to consume the arrival
361
- // parameter, not to mirror every later toggle (`pickMode` already does that).
362
- // eslint-disable-next-line react-hooks/exhaustive-deps
 
 
 
 
 
363
  }, []);
364
 
365
  const pickMode = useCallback((next: AssistantMode) => {
 
54
  * B's to own. The arrival parameter is consumed once, below, and stored.
55
  * Pure and exported so the decision can be checked without a browser.
56
  */
57
+ export function arrivalMode(hash: string): AssistantMode | null {
58
  const mark = hash.indexOf("?");
59
+ if (mark < 0) return null;
60
+ const asked = new URLSearchParams(hash.slice(mark + 1)).get("mode");
61
+ return isMode(asked) ? asked : null;
62
+ }
63
+
64
+ export function initialMode(hash: string, stored: string | null): AssistantMode {
65
+ const asked = arrivalMode(hash);
66
+ if (asked) return asked;
67
  return isMode(stored) ? stored : "chat";
68
  }
69
 
 
356
  * by following a `#/query` link, never touching the toggle, would have the parameter stripped
357
  * here and then land back in Chat on the next reload, restored from a choice made days earlier.
358
  * Following the link IS choosing.
359
+ *
360
+ * β›”β›” AND IT LISTENS FOR `hashchange`, WHICH IS THE DEFECT THE OWNER REPORTED (QA 2026-08-17,
361
+ * measured on the deployed build). `#/query` and `#/assistant` resolve to the SAME route, so
362
+ * following a `#/query` link from anywhere inside the running app is a same-document hash
363
+ * change: this component is already mounted, `useState(readMode)` does not run a second time,
364
+ * and the panel stayed on Chat while the address bar read `?mode=query`. Only a full reload
365
+ * ever showed Query. **A mount-time read cannot see an arrival that does not remount** β€” the
366
+ * listener is the whole fix, and `arrivalMode` is the ONE parser both paths call so a later
367
+ * change cannot make the two disagree.
368
  */
369
  useEffect(() => {
370
+ if (typeof window === "undefined") return;
371
+ const consume = () => {
372
+ const asked = arrivalMode(window.location.hash);
373
+ if (!asked) return;
374
+ setMode(asked);
375
+ storeMode(asked);
376
+ window.history.replaceState(null, "", `#/${ASSISTANT_ROUTE}`);
377
+ };
378
+ consume();
379
+ window.addEventListener("hashchange", consume);
380
+ return () => window.removeEventListener("hashchange", consume);
381
  }, []);
382
 
383
  const pickMode = useCallback((next: AssistantMode) => {
web/src/customer-grid/ColumnMenu.tsx CHANGED
@@ -1647,7 +1647,7 @@ function ExtraTypeEditor({
1647
  <div className="cg-field-hint">
1648
  {dim.keyedBy === "id"
1649
  ? `Pick the column holding the Odoo ID for ${dim.label.toLowerCase()}. Matching on a name instead leaves every cell blank.`
1650
- : `Pick the column holding the ${dim.label.toLowerCase()} value itself β€” this grouping keys by its own value, not by an ID.`}
1651
  </div>
1652
  ) : null}
1653
  <label>
 
1647
  <div className="cg-field-hint">
1648
  {dim.keyedBy === "id"
1649
  ? `Pick the column holding the Odoo ID for ${dim.label.toLowerCase()}. Matching on a name instead leaves every cell blank.`
1650
+ : `Pick the column holding the ${dim.label.toLowerCase()} value itself. This grouping keys by its own value, not by an ID.`}
1651
  </div>
1652
  ) : null}
1653
  <label>
web/src/home/HomePage.tsx CHANGED
@@ -27,6 +27,7 @@ import { FolderMark } from "../customer-grid/icons";
27
  // rather than reached across the fence. Same rule as the rail: consume the component that points
28
  // at the generated artifact, never inline the path ([[loopable-nav-logo-toggle]] β€” one element
29
  // paints the mark).
 
30
  import { Mark } from "../shell/Brand";
31
  import { dbChipClass } from "../shell/nav";
32
  import type { DatabaseEntry, Recent } from "../shell/nav";
@@ -370,7 +371,15 @@ export default function HomePage({
370
  here", which is a different and false statement. */}
371
  {automations.length ? (
372
  <section className="home-section">
373
- <h2 className="home-section-title">Automations</h2>
 
 
 
 
 
 
 
 
374
  <div className={"home-tiles is-" + layout}>
375
  {automations.map((a) => (
376
  <button
 
27
  // rather than reached across the fence. Same rule as the rail: consume the component that points
28
  // at the generated artifact, never inline the path ([[loopable-nav-logo-toggle]] β€” one element
29
  // paints the mark).
30
+ import { AGENTS_MODULE_LABEL } from "../inbox/inboxModel";
31
  import { Mark } from "../shell/Brand";
32
  import { dbChipClass } from "../shell/nav";
33
  import type { DatabaseEntry, Recent } from "../shell/nav";
 
371
  here", which is a different and false statement. */}
372
  {automations.length ? (
373
  <section className="home-section">
374
+ {/* β›” W34-T40's `done-when` names FOUR surfaces that must read Agents, and this was the
375
+ one that did not: a hardcoded `"Automations"` that never consulted the module label.
376
+ QA 2026-08-17. The rail row and the page heading both read `.label` off the registry
377
+ entry, which is why they moved and this did not.
378
+ ⚠ It takes the SHARED constant, not a second literal spelling it correctly today.
379
+ The gate that was supposed to catch this pinned the string `Automations</h2>` and so
380
+ REQUIRED the defect β€” [[a-gate-that-pins-a-spelling-not-a-claim]]. Re-typing "Agents"
381
+ here would have left the next rename in exactly the same trap. */}
382
+ <h2 className="home-section-title">{AGENTS_MODULE_LABEL}</h2>
383
  <div className={"home-tiles is-" + layout}>
384
  {automations.map((a) => (
385
  <button
web/src/shell/Shell.tsx CHANGED
@@ -2000,9 +2000,14 @@ function ShellFrame() {
2000
  Query did not lose its surface β€” it lost its RAIL ROW, because a toggle inside the
2001
  assistant is now the way in. `#/query` still resolves and redirects (`W34-T15`), so
2002
  every existing bookmark and every `openBuiltView` hand-off keeps working.
2003
- ⚠ `SearchIcon` survives as a symbol: the flyout's own filter box draws it. If a later
2004
- change takes that call site too, delete the function in the same edit rather than
2005
- leaving it for `tsc` to find β€” the rail has form here (`PlusMark`, wave 24). */}
 
 
 
 
 
2006
 
2007
  {/* WAVE 19 R10 / C3 β€” Automation, now AGENTS. An `<a>` to the route it already had: the
2008
  surface, its rail and its editor are another session's tree this wave and
@@ -2606,7 +2611,8 @@ function ShellFrame() {
2606
  {...(active.icon ? { icon: active.icon } : {})}
2607
  />
2608
  <div className="shell-auto-host">
2609
- <Lazily surface="Automation"><AutomationSurface /></Lazily>
 
2610
  </div>
2611
  </div>
2612
  ) : active?.kind === "native" ? (
 
2000
  Query did not lose its surface β€” it lost its RAIL ROW, because a toggle inside the
2001
  assistant is now the way in. `#/query` still resolves and redirects (`W34-T15`), so
2002
  every existing bookmark and every `openBuiltView` hand-off keeps working.
2003
+ ⚠ THIS COMMENT ASSERTED SOMETHING FALSE FOR ABOUT A MINUTE, and the correction is
2004
+ kept because the habit is the useful part. It read *"`SearchIcon` survives as a
2005
+ symbol: the flyout's own filter box draws it"* β€” it does not, and `tsc` (TS6133) said
2006
+ so on the very next run. The function was deleted in the same edit; its tombstone is
2007
+ where it used to live, one screen up. **A claim about who calls a symbol is
2008
+ answerable in one command, so ask the compiler rather than reasoning about it.**
2009
+ Found again at the close-out dead-code sweep, which greps every deleted symbol and
2010
+ reads each survivor: this one was a stale CLAIM rather than a live caller. */}
2011
 
2012
  {/* WAVE 19 R10 / C3 β€” Automation, now AGENTS. An `<a>` to the route it already had: the
2013
  surface, its rail and its editor are another session's tree this wave and
 
2611
  {...(active.icon ? { icon: active.icon } : {})}
2612
  />
2613
  <div className="shell-auto-host">
2614
+ {/* USER-VISIBLE: ErrorBoundary renders "{surface} could not be shown" (T40, QA) */}
2615
+ <Lazily surface="Agents"><AutomationSurface /></Lazily>
2616
  </div>
2617
  </div>
2618
  ) : active?.kind === "native" ? (