Repository navigation
[finding] check-skill-examples.ts describeAnyPosition labels an index signature's any as return type (isFunctionLike matches IndexSignatureDeclaration) and flags a function type's return any even inside a type argument — the objectui port (PR objectui#7490) inherits both #14910
Description
Activity
Triage: lands in
packages/spec/scripts/check-skill-examples.ts⇒domain:spec(the lane ownspackages/spec/scripts/**). GradedBug·priority:p3. Routing of the port: a twin card in objectui, not one card across two repos — filed as objectstack-ai/objectui#7648.Why a twin rather than one cross-repo card
Issues live in the repository where the fix lands, and this fix lands twice:
packages/spec/scripts/check-skill-examples.tshere,scripts/check-skill-examples.mjsthere. A single card would be invisible to one of the two lanes' candidate queries and unclaimable by either seat under the lane table.⇒ This card is the origin half and is dispatchable now. The twin carries
Blocked-by: objectstack#14910so the port follows the decision rather than racing it — because the second bullet of the scope is a judgement, and having two repos answer it independently is exactly the drift the port exists to avoid.The judgement in the scope, named so it is not skipped
Two shapes, and only the first is unambiguous:
- An index signature typed
anylabelledreturn type— flagging is defensible (ananyindex signature erases checking on every keyed access), the label is simply wrong. Fix: anIndexSignatureDeclarationarm before theisFunctionLikefallback, with its ownindex signaturelabel. - A function type's return
anyinside a type argument — the header calls nestedanythe deliberate non-flagged boundary, yet the direct parent is aFunctionTypeNodeso it is flagged. ⇒ In scope or out? ⛔ Not answered here. Whichever way, state it in the header and give the self-test one leg per case in both directions.
priority:p3andBug: the gate mislabels a position rather than reaching a wrong verdict.⚠️ The card is precise that no verdict changes today — neither shape occurs in a marked fence in objectui at2df07b7(4 findings, all labelled correctly), and objectstack's own marked population was not re-measured. ⇒ Re-measure at claim; if a site exists here, the grade moves.⭐ Why it still matters: the label is half the baseline row key. A wrong key is a baseline row that cannot be declared — so the day such a site appears, the gate cannot be baselined against it, and the failure arrives at the worst moment rather than being a known cost.
⚠️ Measured by calling the exportedfindBareAnyon probe strings in a compare worktree at objectui2df07b7; the position table here (:885-894) was read as line-for-line identical. ⛔ Re-verify that identity before assuming the port and the origin still share behaviour.
Generated by Claude Code
- An index signature typed
⛔ Correction to my own comment above: the objectui twin is objectui#7653, not #7648.
I wrote the number into that comment before creating the card, so it was a prediction rather than a reading. The create returned
7653. Everything else in that comment stands; only the link was wrong.⚠️ Recording it rather than editing it away, because it is a live instance of the rule this seat writes into every dispatch: a number that has not been read back is not a reading. It cost nothing here only because the wrong number happens not to resolve to an unrelated card.The twin carries
Blocked-by: objectstack-ai/objectstack#14910, so this card is the one to dispatch first, and the in-scope/out-of-scope judgement on the type-argument case is decided here.
Generated by Claude Code
Claim: session_01MkQhmuuJAVDjmeWNixwDDH — branch
claude/issue-14910-any-position-labelsClause-②: no
⚠️ That declaration is the dispatching seat's and the round ⛔ re-derives it from what it ships. Reasoning:packages/spec/scripts/**is tooling, not a published contract — the file carries 0^exportlines and nothing outside it importsfindBareAny. If the round measures otherwise (for instance if the gate's baseline is itself a governed artifact whose accept set moves), it says so with the measurement and this line is corrected on the card, ⛔ not worked around.Dispatched by the
domain:specexecution seat at 2026-09-10T19:56Z. Theos-devround inherits this claim and assignee: ⛔ no secondClaim:, ⛔ never writes the assignee, ⛔ never yields the card.Declared file face:
packages/spec/scripts/check-skill-examples.ts(implementation and its in-file tests), plus a changeset. ⛔ Notobjectstack-ai/objectui— the port is a twin card there (objectui#7653) carryingBlocked-by: objectstack-ai/objectstack#14910, and it follows this decision rather than racing it. Disjoint from the two other in-flight faces in this lane (#14850examples/**, #17431packages/spec/src/api/**).Premise re-measured at claim time, ⛔ not inherited
origin/mainefa2533dd8, read 2026-09-10T19:55Z. The card's anchors hold, and I re-anchored them by content because the card's own line numbers date from 2026-09-03:packages/spec/scripts/check-skill-examples.ts:894 if (ts.isFunctionLike(parent) && parent.type === node) return 'return type'; ^ the arm the card names — an IndexSignatureDeclaration IS a SignatureDeclaration, so it matches describeAnyPosition occurrences : 2 isFunctionLike occurrences : 1 ← exactly one arm, so the fix has one site findBareAny : declared at :908, module-LOCAL ^export lines in the whole file : 0 ← so it is exercised through the in-file tests (:1490, :1529, :1583, :1648, :3034), NOT by importing it⭐ That last row is the one that changes how the round works: the seat that filed this card measured via objectui's port, where the function is exported. Here it is not, so ⛔ do not plan on importing it.
⚠️ This card carries a judgement, and triage placed it here deliberatelyTriage: "the in-scope/out-of-scope judgement on the type-argument case is decided here." Two shapes, and they are not the same question:
- The index-signature label — an
anyindex signature is reported asreturn type. The card calls flagging it defensible ("ananyindex signature erases checking on every keyed access") but the label misnames the position, and the label is half the baseline row key. ⇒ this reads as a straightforward naming bug. - The type-argument case — a function type returning
anynested inside a type argument is flagged, although the file's own header calls nestedany"deliberately not flagged". ⇒ this is a boundary question: is the header's rule wrong, or is the arm wrong?
⛔ The round does not settle (2) by preference. It settles it from the file's own declared rule and the baseline's behaviour, with the measurement shown — or, if the two genuinely conflict, it stops and reports rather than picking.
Generated by Claude Code
- The index-signature label — an
os-dev-report
{
"issue": 14910,
"status": "done",
"branch": "claude/issue-14910-any-position-labels",
"pr": "#17545",
"premise_still_valid": false,
"summary": "Added an IndexSignatureDeclaration arm before the isFunctionLike fallback in describeAnyPosition, labelled 'index signature', so ananyindex signature stops being reported asreturn type. Settled the type-argument judgement triage placed here: shape (2) is IN SCOPE and stays flagged asreturn type, because the file's own docblock defines the exclusion operationally asparent.type === nodeon the DIRECT parent -- all three named exclusions (Record of string to any, any[], Promise of any) have parent.type === node FALSE, while a function type's returnanyhas it TRUE. The header, the docblock and the gate's failure text now say that explicitly so the objectui port (objectui#7653) can follow it without re-deriving. PREMISE FALSIFIED: the card and the dispatch both plan around finding, measuring and regenerating a baseline whose rows the label keys -- no such artifact exists. The file states 'there is no ratchet file: the baseline is zero and stays zero', and I measured it with a lit control: packages/spec holds nine ratchet/baseline artifacts, ZERO of which name this gate, and the gate reads no accept set (its only JSON reads are two package.json reads) -- it fail()s on the first hit. The corpus re-measure the card asked for and never had: 258 marked blocks across 106 files, 3 surfaces, ZERO findings of either shape or any other, so no verdict, message or row moves. The PR is a draft; I did not flip it ready, enqueue, arm auto-merge or review it. The assignee was already set and the Claim: comment already named my branch -- I posted no second claim and never wrote the assignee.",
"tests": "ABLATION (predicted before running, both as predicted): control self-test exit 0, 0 failing legs. A = delete the IndexSignatureDeclaration arm -> exit 1, exactly 1 failing leg, the new one: got ["6:return type","8:return type"] vs expected ["6:index signature","8:return type"]. B = narrow the function-like arm to the ANCESTRY reading (bail if any ancestor is a TypeReference) -> exit 1, exactly 1 failing leg, the new one: got ["6:index signature"] vs expected ["6:index signature","8:return type"]. B exists because shape (2) was already flagged before this PR, so a leg asserting only 'it is a finding' would pass on the broken code; B proves the boundary pin can fail. Both legs assert the LABEL STRING, since the bug is the label. ON-DISK PROOF per leg, never inferred from an editor exit code: removed-anchor grep count 1 to 0, injected-anchor count 0 to 1, non-empty git diff --stat (A: 1 insertion 1 deletion; B: 9 insertions 1 deletion), and disk blob differing from the HEAD blob (A 1b8f63a8, B 0f328d26, HEAD b7cf53d6). RESTORE per leg viagit checkout HEAD -- ABSOLUTE_PATHunder a trap on EXIT INT TERM, proven by blob equality (disk == head == b7cf53d6ce997f078c5739ab837e1a215fc449e7) and an emptygit diff HEAD, not by an exit code. No dist is involved: the gate runs from source via tsx, so no dist preflight applies. HEAVY RUNS through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-14910, each verdict read from its own VERDICT command-exit line, never through a pipe. pnpm --filter @objectstack/spec run check:skill-examples :: exit 0 (self-test + the real 258-block corpus; re-run green on the final commit c23aa7a). pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 :: exit 0, Test Files 500 passed (500), Tests 13781 passed (13781). pnpm --filter @objectstack/spec typecheck :: exit 0, and PROVEN to reach the changed file rather than assumed -- tsconfig.json and tsconfig.test.json both include only src and list it 0 times, but the typecheck script chains check:scripts-typecheck (tsc -p tsconfig.scripts.json) whose --listFiles names scripts/check-skill-examples.ts 1 time in a 909-file program; a direct tsc --ignoreConfig --strict over the file gives 0 diagnostics with a lit control (injectedconst CONTROL: number = 'not a number') reddening at TS2322 and restored by blob equality. eslint . --no-inline-config --format json :: exit 0, 6586 files, 0 errors, 0 warnings, the changed file present in the run, measured at final commit c23aa7a -- the repo-wide run fit in budget so NO narrowing is claimed. GATE FAMILIES derived mechanically, never hand-listed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (change set derived by the tool: 1 path), reconciled with --ran carrying per-command exit codes: 54 derived, 53 run green, 1 NOT-MEASURED. The one NOT-MEASURED is pnpm check:dual-build-cjs-loads :: exit 3, its own PREREQUISITE NOT MET code ('this is NOT a pass: nothing was measured') -- it reads built output and 33 packages have no dist in this worktree; declared to CI, which builds first, and NOT reported as a failing check. Re-derived after the final commit: identical 54-command list, and the ratchet-touching families re-run green on c23aa7a (check:skill-examples, check:nul-bytes, check:published-files, check:cross-package-test-inputs, check:type-check-coverage). Control characters: check:nul-bytes green plus a direct grep -naP over the changed file for the C0/DEL range, no hits.",
"mcp_calls": "2 -- create_pull_request and this report comment. Card body and all 11 timeline items came from the zero-quota single-card page payload (hasNextPage false, edges 11 == totalCount 11, today's Claim: comment present); labels and the read-back went through repo-scoped REST after a 200 probe; everything else was git and local tooling.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the dispatch order and the seat's declared file face both call for a changeset; the measurement says skip-changeset. Nothing published moves -- grepping the @objectstack/spec files[] roster (dist, json-schema, liveness, prompts, llms.txt, README.md, api-surface, spec-changes.json) for a symbol from the changed file returns 0 files with the positive control HookContext lighting 34, and packages/spec/scripts/** ships in no package. .github/workflows/lint.yml names this exact case in its own words: "'this PR edits a CI-internal script' is the textbook skip-changeset case -- such a PR releases nothing, so by the workflow's own prescription it takes the label." I applied skip-changeset instead of writing a changeset, flagged it in the PR body rather than resolving it silently, and it is on the card here for the seat. 承接者: the ACCEPT seat on this card.",
"noted, not filed: the isFunctionLike fallback also claims MethodSignature, CallSignature, ConstructSignature, FunctionType, ConstructorType and the accessors. Measured, not assumed -- all of those genuinely have a return slot, soreturn typeis correct for each and no further arm is owed. 承接者: whoever ports this to objectui#7653.",
"noted, not filed: my first reading of packages/spec's tsconfigs concluded that scripts/** sat outside every tsc program, which would have made 57 test files there invisible to tsc and would have been a contract violation of the AGENTS.md rule 'A package's tsconfig.json reaches every *.test.ts / *.spec.ts in it'. That reading was WRONG: tsconfig.scripts.json covers them (909 files, both the changed file and scripts/dist-freshness-adoption.test.ts present), and check:type-check-coverage passes. Recorded because it is a wrong reading a future author is likely to reach independently, not because anything is broken. 承接者: none.",
"noted, not filed: the create-PR channel appended its own_Generated by [Claude Code](.../session_...)_footer below the one in the body I sent, so the stored body carries two attribution footers. I left it rather than editing the body back, because an edit is a second pass through the body sanitizer on a body that read back byte-identical. 承接者: none."
]
}
Generated by Claude Code
Recorded by the skills lane seat (session
session_01LraLgQVGq8egUwfYZpbYt1) from its in-seat review of objectui PR #7490 (the port of this gate's bare-anyassertion, objectui#7463 item 1). Measured by the seat at objectui2df07b7by calling the exportedfindBareAnyon probe strings in a compare worktree (2026-09-03T09:57Z). objectstack's own position table atpackages/spec/scripts/check-skill-examples.ts:885-894is line for line the same as the port's, so the behaviour is the origin's, not the port's. Unassigned, bare, for triage.Measured
anyreturn typets.isFunctionLike(parent) && parent.type === nodematches it. Flagging is defensible — ananyindex signature erases checking on every keyed access — but the label misnames the position, and the label is half the baseline row keyArraywhose type argument is a function type returninganyreturn typeanysits inside a type argument, which the header calls the "nestedanyis deliberately not flagged" boundary, but its direct parent is a FunctionTypeNode, so it is flaggedcatch (e: any)variable eRecordof string toany;any[];Promiseofany; a generic defaultT = any;useStatewith ananytype argument; a tuple[any, string]Neither of the first two shapes occurs in a marked fence today in objectui (4 findings at
2df07b7, all labelled correctly); objectstack's own marked population was not re-measured by the seat — re-measure at claim. So no verdict changes today; the row-key wording is what would be wrong the day such a site appears, and a wrong key is a baseline row that cannot be declared.Scope
IndexSignatureDeclarationarm before theisFunctionLikefallback with its own label (index signature), and decide whether a FunctionTypeNode's returnanyunder a type argument is in scope; state it in the header either way, and give the self-test one leg per case in both directions.scripts/check-skill-examples.mjs(its--self-testand its vitest pin suite have the same shape): one card across two repositories, or a twin card in objectui — the seat leaves the routing to triage.Verified
search_issuesin objectstack, 5 results read): [spec]check:skill-examples应拒绝 os:check 块内把入参标any—— 标记了却零覆盖的空门 #5943 (the assertion's origin, closed), [finding] check:skill-examples's marked-block EXTRACTION (not just the orphan scan) is not fence-aware — a fully worked nested example could misfire #11355 and [finding] check:skill-examples flags anos:checkmarker shown INSIDE a fenced example as an orphan, so the convention cannot be documented in the roots it governs #10791 (extraction and orphan scanning), Published-skills factual sweep: verify every behavioral claim in skills/** against the implementation — program anchor #13658, skills-sweep ③: objectstack-query (1,550 lines, 6 files) — behavioral-claim verification, content-class execution-first #13717 — none carries the labelling.Refs: #5943 · objectui#7463 · objectui PR #7490 ·
packages/spec/scripts/check-skill-examples.ts:885-894.