You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Resolve the spec-named symbol collisions with 17.0.0 GA exports (check:spec-symbols): SECRET_MASK + ObjectCalendarProps/ObjectFormProps/ObjectGridProps/ObjectKanbanProps + one more #4650
Filed and claimed by the repo:objectui seat (session session_01RnQd8iMMUwXQEV1crFmQiQ, 2026-08-14). Pre-bump adaptation card (2 of 2) for #4636 / PR #4639, from that dev's measurement; graded straight to dispatch: it restores the #4043 doctrine (a spec-named symbol must be an import, not a declaration) ahead of the bump, and blocks it.
Context
@objectstack/spec@17.0.0 exports names that objectui currently declares locally, so check-spec-symbol-derivation exits 1 on the GA branch (PR #4639) with six collisions; five are named in that PR's body:
The sixth was not named — derive the authoritative list by running the gate script against a tree with GA installed (the PR #4639 branch works), and record it in the PR body.
Direction (per symbol, judgment required — this is why the card is opus-tier)
Different thing sharing a name (likely for the *Props React component-prop interfaces vs the spec's schema-props exports) ⇒ rename the local, keeping the old name as a deprecated re-export alias so no published API breaks in a patch; note each alias in the changeset.
All choices must keep current main (rc.6) green — the gate only fires on GA, so a correct fix is invisible to today's farm except via types/tests you adjust.
Claim: PM loop round 2 (filed-and-claimed same round; grading rationale in the body)
Session: session_01RnQd8iMMUwXQEV1crFmQiQ
Branch: claude/issue-4650-spec-symbols
Worktree: objectui-issue-4650
Domain: repo:objectui
File surface: packages/plugin-calendar/src/ObjectCalendar.tsx, packages/plugin-form/src/ObjectForm.tsx, packages/plugin-grid/src/ObjectGrid.tsx, packages/plugin-kanban/src/ObjectKanban.tsx, packages/app-shell/src/views/metadata-admin/widgets.tsx + the sixth colliding site once derived + those packages' barrels for deprecated re-export aliases (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus
Serial constraints cleared: in-flight #4649 shares packages/app-shell/src/views/ at the package level but declares only ObjectView.tsx (disjoint file, named mutually) and touches plugin-list/plugin-view (no package overlap with this card's four plugins); #4632 declares types/disclosure + components/disclosure renderer (disjoint); no open dev PR touches these files (checked this round).
{
"issue": 4650,
"status": "done",
"branch": "claude/issue-4650-spec-symbols",
"pr": "https://github.com/objectstack-ai/objectui/pull/4660",
"premise_still_valid": true,
"summary": "All six spec-named symbol collisions resolved; check-spec-symbol-derivation is green on a GA-installed tree AND on current main's rc.6 pin, both readings taken at the pushed HEAD 66adc24fa. The unnamed sixth is packages/plugin-calendar/src/index.tsx:21 — `export type { ObjectCalendarProps };` with no `from` clause, which the gate's barrel-skip cannot recognise, so it is judged as its own declaration. The four Object*Props are DIFFERENT things from the spec's (spec = authored props documents, z.input of Object*PropsSchema; ours = renderer props with a live dataSource and host callbacks), so each was renamed to Object*ComponentProps with the old name kept as a @deprecated barrel alias spelled `export type { New as Old } from './Module'` — the from clause is what keeps the alias one judgement instead of a fresh collision. SECRET_MASK is genuinely THE SAME constant the spec now owns (ADR-0100, objectstack#7572), so the doctrine's preferred arm is to import it; that import is un-writable on the rc.6 pin, and 'must land green on current main' is the 裁决 that wins, so it is renamed to OBJECTUI_SECRET_MASK with the one-line burn-down recorded at the declaration for whoever lands #4639. Nothing published breaks: SECRET_MASK is not reachable from app-shell's barrel (index.ts re-exports a named list from ./views/metadata-admin, that barrel does not re-export widgets, no export * on either path), so no alias was owed and none was added. Naming follows repo precedent rather than the gate's generic ObjectUiLocalX suggestion: PageHeaderProps -> PageHeaderComponentProps in app-shell's own rename table, and the Record*ComponentProps family in @object-ui/types.",
"tests": "All at HEAD 66adc24fa (git status clean; remote branch at the same sha). GATE, BOTH DIRECTIONS — rc.6 pin: '1252 files scanned against 4834 spec export names; 13 declared dialects, 3 untriaged collisions in 1 packages' + '2 declared deliberate copies, 18 unbacked claims in 5 packages', exit 0 (identical before and after my change — the collisions are GA-only). GA overlay (@objectstack/spec@17.0.0): same shape against 4912 export names, exit 0; on origin/main the same command is exit 1 listing exactly the six. The differing name counts (4834 vs 4912) are the proof the two readings really are different export sets, and both keep all 13 ALLOW entries matched and the same 3 @object-ui/types DEBT entries live, so no exemption went stale in either direction. GA MEASUREMENT METHOD: npm pack @objectstack/spec@17.0.0 unpacked into the worktree's node_modules/.ga-measure/ with the @objectstack/spec symlink repointed at it, so the real gate ran unmodified; placed INSIDE the worktree so zod and friends resolve by walking up to the workspace root (outside it the .d.ts degrade toward any and under-report names). rc.6 link restored afterwards, verified by re-resolving the version. OTHER GATES, all PASS: check:phantom-deps ('2162 tooling import(s) are declared only by the workspace root' — this is what makes the new tests' spec import legal in plugin-calendar/plugin-kanban without a package.json change), check:control-bytes, check:action-forward-parity, check:i18n-keys, check:i18n-drift, check:skills-paths, check-changeset-presence ('17 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-fixed, check-changeset-no-major, eslint --quiet over the changed files (0 errors). BUILD + TYPES: dependency closure built first (pnpm --filter '<pkg>^...' build, exit 0); type-check green for the four plugins and app-shell (each runs tsc --noEmit && tsc -p tsconfig.test.json, so the compile-time pins are really compiled). VITEST: 302 files / 2875 passed, 1 skipped, exit 0 over the touched packages; then 8 files / 61 passed at the final HEAD over the new and touched test files. REVERSE VERIFICATION (direction predicted before running, both red as predicted, both restored from the commit and re-confirmed green with a clean git status = byte-identical): pointing the @ts-expect-error at a name the pinned spec really exports gave 'error TS2578: Unused @ts-expect-error directive'; weakening the alias pin to Equal<ObjectGridProps, string> gave 'error TS2344: Type false does not satisfy the constraint true'. No ablation involved dist/, so no rebuild applies. NOT RUN: the full 55+ gate farm and full-repo vitest — CI's job.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #4661: packages/plugin-form/src/ObjectForm.tsx opens the foldFormButtons doc comment with a doubled `/**` (pre-existing on origin/main @ a99233fff; cosmetic, no tool has an opinion about it, labelled `finding` with no pm:queue and framed as a fair WONTFIX)"
]
}
Notes for the PM that do not fit the schema
One clause tension worth your eye, resolved but not hidden. The card's per-symbol rule says same thing ⇒ derive (import + delete local). SECRET_MASKis the same thing, so the rule points at an import — which cannot be written while the pin is ^17.0.0-rc.6, because that release does not export the name. The 裁决 "everything must land green on current main" is what decides it. The rename is therefore a stand-in, not the end state, and the burn-down is one line recorded at the declaration: when chore(deps): raise the @objectstack/* pin from ^17.0.0-rc.6 to 17.0.0 GA #4639 lands, replace the literal with a re-export from @objectstack/spec/data. Worth attaching to chore: raise the @objectstack/spec pin from ^17.0.0-rc.6 to 17.0.0 GA (lockfile resolution + green-keeping adaptations) #4636's follow-up list so it is not lost.
A tripwire ratchet that is deliberately inert today. app-shell's rename table gained a RENAMES_PENDING_GA half whose "the spec still owns the old name" assertion is FALSE against rc.6 and TRUE against GA. It reads the installed spec's version and arms itself the moment the pin stops being a pre-release — so the bump PR will find it already asserting, with no edit needed.
One lap lost, recorded so the next agent does not repeat it. The natural spelling for the tripwire, import type * as SpecUi from '@objectstack/spec/ui', is an ESLint error in this repo: a namespace import of that subpath necessarily binds FormField/FormFieldSchema, which are restricted because the spec's form-view types erase to any (objectui#3090). Re-spelled as a type-level import('@objectstack/spec/ui').X, which names one member and declares no binding. The reason is written into each of the four test files so it is not "cleaned up" back into the banned form.
Labels read back after bots settled: plugin, plugin: kanban, tests. No skip-changeset, which is correct — this PR changes src/ of five released packages and carries a changeset.
ACCEPT — PR #4660 (reviewer of record: seat session session_01RnQd8iMMUwXQEV1crFmQiQ).
Verified against GitHub: 18 changed files, all inside the claimed surface (the four plugins + app-shell metadata-admin/widgets.tsx + barrels + tripwire tests + one minor changeset); siblings' regions untouched as declared. The disposition table holds up: the four Object*Props are measurably different things from the spec's exports (authored props documents vs renderer props) ⇒ rename to the repo's own *ComponentProps precedent with @deprecated aliases whose from clauses are load-bearing (the unnamed sixth collision was exactly the no-from barrel shape, and the PR explains why); SECRET_MASK is the same constant ⇒ derive is right but un-writable on rc.6, so rename + a one-line burn-down at the declaration, with no alias because the constant is provably unreachable from the published barrel (checked in the diff). Gate evidence is double-sided (rc.6 pin AND GA overlay, both exit 0 at the pushed head, with the 4834-vs-4912 export-count delta proving the readings differ; origin/main under the same overlay is exit 1 with exactly the six). The self-arming RENAMES_PENDING_GA tripwire and the deliberate refusal to add spec devDep pins to two plugins (avoiding new pins for PR #4639 to raise) are both accepted as designed.
Out-of-scope product listed for grading: #4661 (cosmetic doubled /**, pre-existing — filed as a fair-WONTFIX-framed finding).
Landing: both adaptation PRs (#4657, #4660) flip ready + auto-merge on gate-green — they are file-disjoint by claim and verified disjoint in the diffs, so both can queue in the same window. Once both are MERGED, the pin-bump PR #4639's remaining blockers reduce to the #4648 ruling (maintainer inbox) plus the re-point.
Activity
yinlianghui commentedon Aug 14, 2026
Claim: PM loop round 2 (filed-and-claimed same round; grading rationale in the body)
Session:
session_01RnQd8iMMUwXQEV1crFmQiQBranch:
claude/issue-4650-spec-symbolsWorktree:
objectui-issue-4650Domain:
repo:objectuiFile surface:
packages/plugin-calendar/src/ObjectCalendar.tsx,packages/plugin-form/src/ObjectForm.tsx,packages/plugin-grid/src/ObjectGrid.tsx,packages/plugin-kanban/src/ObjectKanban.tsx,packages/app-shell/src/views/metadata-admin/widgets.tsx+ the sixth colliding site once derived + those packages' barrels for deprecated re-export aliases (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opusSerial constraints cleared: in-flight #4649 shares
packages/app-shell/src/views/at the package level but declares onlyObjectView.tsx(disjoint file, named mutually) and touches plugin-list/plugin-view (no package overlap with this card's four plugins); #4632 declares types/disclosure + components/disclosure renderer (disjoint); no open dev PR touches these files (checked this round).Generated by Claude Code
yinlianghui commentedon Aug 14, 2026
{ "issue": 4650, "status": "done", "branch": "claude/issue-4650-spec-symbols", "pr": "https://github.com/objectstack-ai/objectui/pull/4660", "premise_still_valid": true, "summary": "All six spec-named symbol collisions resolved; check-spec-symbol-derivation is green on a GA-installed tree AND on current main's rc.6 pin, both readings taken at the pushed HEAD 66adc24fa. The unnamed sixth is packages/plugin-calendar/src/index.tsx:21 — `export type { ObjectCalendarProps };` with no `from` clause, which the gate's barrel-skip cannot recognise, so it is judged as its own declaration. The four Object*Props are DIFFERENT things from the spec's (spec = authored props documents, z.input of Object*PropsSchema; ours = renderer props with a live dataSource and host callbacks), so each was renamed to Object*ComponentProps with the old name kept as a @deprecated barrel alias spelled `export type { New as Old } from './Module'` — the from clause is what keeps the alias one judgement instead of a fresh collision. SECRET_MASK is genuinely THE SAME constant the spec now owns (ADR-0100, objectstack#7572), so the doctrine's preferred arm is to import it; that import is un-writable on the rc.6 pin, and 'must land green on current main' is the 裁决 that wins, so it is renamed to OBJECTUI_SECRET_MASK with the one-line burn-down recorded at the declaration for whoever lands #4639. Nothing published breaks: SECRET_MASK is not reachable from app-shell's barrel (index.ts re-exports a named list from ./views/metadata-admin, that barrel does not re-export widgets, no export * on either path), so no alias was owed and none was added. Naming follows repo precedent rather than the gate's generic ObjectUiLocalX suggestion: PageHeaderProps -> PageHeaderComponentProps in app-shell's own rename table, and the Record*ComponentProps family in @object-ui/types.", "tests": "All at HEAD 66adc24fa (git status clean; remote branch at the same sha). GATE, BOTH DIRECTIONS — rc.6 pin: '1252 files scanned against 4834 spec export names; 13 declared dialects, 3 untriaged collisions in 1 packages' + '2 declared deliberate copies, 18 unbacked claims in 5 packages', exit 0 (identical before and after my change — the collisions are GA-only). GA overlay (@objectstack/spec@17.0.0): same shape against 4912 export names, exit 0; on origin/main the same command is exit 1 listing exactly the six. The differing name counts (4834 vs 4912) are the proof the two readings really are different export sets, and both keep all 13 ALLOW entries matched and the same 3 @object-ui/types DEBT entries live, so no exemption went stale in either direction. GA MEASUREMENT METHOD: npm pack @objectstack/spec@17.0.0 unpacked into the worktree's node_modules/.ga-measure/ with the @objectstack/spec symlink repointed at it, so the real gate ran unmodified; placed INSIDE the worktree so zod and friends resolve by walking up to the workspace root (outside it the .d.ts degrade toward any and under-report names). rc.6 link restored afterwards, verified by re-resolving the version. OTHER GATES, all PASS: check:phantom-deps ('2162 tooling import(s) are declared only by the workspace root' — this is what makes the new tests' spec import legal in plugin-calendar/plugin-kanban without a package.json change), check:control-bytes, check:action-forward-parity, check:i18n-keys, check:i18n-drift, check:skills-paths, check-changeset-presence ('17 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-fixed, check-changeset-no-major, eslint --quiet over the changed files (0 errors). BUILD + TYPES: dependency closure built first (pnpm --filter '<pkg>^...' build, exit 0); type-check green for the four plugins and app-shell (each runs tsc --noEmit && tsc -p tsconfig.test.json, so the compile-time pins are really compiled). VITEST: 302 files / 2875 passed, 1 skipped, exit 0 over the touched packages; then 8 files / 61 passed at the final HEAD over the new and touched test files. REVERSE VERIFICATION (direction predicted before running, both red as predicted, both restored from the commit and re-confirmed green with a clean git status = byte-identical): pointing the @ts-expect-error at a name the pinned spec really exports gave 'error TS2578: Unused @ts-expect-error directive'; weakening the alias pin to Equal<ObjectGridProps, string> gave 'error TS2344: Type false does not satisfy the constraint true'. No ablation involved dist/, so no rebuild applies. NOT RUN: the full 55+ gate farm and full-repo vitest — CI's job.", "open_questions": [], "out_of_scope_findings": [ "filed as #4661: packages/plugin-form/src/ObjectForm.tsx opens the foldFormButtons doc comment with a doubled `/**` (pre-existing on origin/main @ a99233fff; cosmetic, no tool has an opinion about it, labelled `finding` with no pm:queue and framed as a fair WONTFIX)" ] }Notes for the PM that do not fit the schema
One clause tension worth your eye, resolved but not hidden. The card's per-symbol rule says same thing ⇒ derive (import + delete local).
SECRET_MASKis the same thing, so the rule points at an import — which cannot be written while the pin is^17.0.0-rc.6, because that release does not export the name. The 裁决 "everything must land green on current main" is what decides it. The rename is therefore a stand-in, not the end state, and the burn-down is one line recorded at the declaration: when chore(deps): raise the @objectstack/* pin from ^17.0.0-rc.6 to 17.0.0 GA #4639 lands, replace the literal with a re-export from@objectstack/spec/data. Worth attaching to chore: raise the@objectstack/specpin from^17.0.0-rc.6to 17.0.0 GA (lockfile resolution + green-keeping adaptations) #4636's follow-up list so it is not lost.A tripwire ratchet that is deliberately inert today. app-shell's rename table gained a
RENAMES_PENDING_GAhalf whose "the spec still owns the old name" assertion is FALSE against rc.6 and TRUE against GA. It reads the installed spec's version and arms itself the moment the pin stops being a pre-release — so the bump PR will find it already asserting, with no edit needed.One lap lost, recorded so the next agent does not repeat it. The natural spelling for the tripwire,
import type * as SpecUi from '@objectstack/spec/ui', is an ESLint error in this repo: a namespace import of that subpath necessarily bindsFormField/FormFieldSchema, which are restricted because the spec's form-view types erase toany(objectui#3090). Re-spelled as a type-levelimport('@objectstack/spec/ui').X, which names one member and declares no binding. The reason is written into each of the four test files so it is not "cleaned up" back into the banned form.Scope held. Nothing touched in
app-shell/src/views/ObjectView.tsx, plugin-list or plugin-view (Unwind the objectstack#7176 striped/bordered/virtualScroll forwarding chain (types → react spec-bridge → plugin-list → plugin-view → app-shell) + drop ObjectGrid.component.yml's retired declarations #4649), or types/disclosure and components/disclosure (finding(components): ToggleGroupItem.icon is declared in types but no renderer reads it — authored icons render nothing #4632). Nopackage.jsonand no lockfile change, deliberately — a new spec devDep on plugin-calendar/plugin-kanban at the rc range would be a fifth and sixth pin for PR chore(deps): raise the @objectstack/* pin from ^17.0.0-rc.6 to 17.0.0 GA #4639 to find and raise.Labels read back after bots settled:
plugin,plugin: kanban,tests. Noskip-changeset, which is correct — this PR changessrc/of five released packages and carries a changeset.Generated by Claude Code
yinlianghui commentedon Aug 14, 2026
ACCEPT — PR #4660 (reviewer of record: seat session
session_01RnQd8iMMUwXQEV1crFmQiQ).Verified against GitHub: 18 changed files, all inside the claimed surface (the four plugins + app-shell
metadata-admin/widgets.tsx+ barrels + tripwire tests + oneminorchangeset); siblings' regions untouched as declared. The disposition table holds up: the fourObject*Propsare measurably different things from the spec's exports (authored props documents vs renderer props) ⇒ rename to the repo's own*ComponentPropsprecedent with@deprecatedaliases whosefromclauses are load-bearing (the unnamed sixth collision was exactly the no-frombarrel shape, and the PR explains why);SECRET_MASKis the same constant ⇒ derive is right but un-writable on rc.6, so rename + a one-line burn-down at the declaration, with no alias because the constant is provably unreachable from the published barrel (checked in the diff). Gate evidence is double-sided (rc.6 pin AND GA overlay, both exit 0 at the pushed head, with the 4834-vs-4912 export-count delta proving the readings differ;origin/mainunder the same overlay is exit 1 with exactly the six). The self-armingRENAMES_PENDING_GAtripwire and the deliberate refusal to add spec devDep pins to two plugins (avoiding new pins for PR #4639 to raise) are both accepted as designed.Out-of-scope product listed for grading: #4661 (cosmetic doubled
/**, pre-existing — filed as a fair-WONTFIX-framedfinding).Landing: both adaptation PRs (#4657, #4660) flip ready + auto-merge on gate-green — they are file-disjoint by claim and verified disjoint in the diffs, so both can queue in the same window. Once both are MERGED, the pin-bump PR #4639's remaining blockers reduce to the #4648 ruling (maintainer inbox) plus the re-point.
Generated by Claude Code
Merge origin/main into claude/issue-4636-spec-pin-ga (re-point onto p…