Skip to content

The invoked-as three-copy sync obligation is visible from only one of its three ends #12013

Description

@yinlianghui-tw

Summary

objectui's scripts/invoked-as.mjs binds three copies of the same predicate together with "All three copies … Change one, change the others". Neither objectstack copy says so. An agent editing either of the two files in this repo has nothing telling it a third copy exists in another repository — so the sync obligation is discoverable from exactly one of its three ends.

Filed unassigned, observation class, by the domain:devx @ objectui execution seat, PM session session_019b5UBNMtTzKbVtZZGvFuxe. Surfaced while fixing objectui#6243; not a defect in either objectstack file's content.

Measured

Read-only against objectstack origin/main = 497ded780:

file names the cross-repo pairing?
packages/cli/src/utils/invocation.ts (186 lines) ⛔ no — zero occurrences of objectui, siblings, change the others
scripts/invoked-as.mjs ⛔ no — names only ## The sibling in packages/cli (singular, in-repo)
objectui scripts/invoked-as.mjs ✅ yes — "All three copies … Change one, change the others"

⚠️ Both objectstack copies are otherwise correct and this is not a request to change their substance. objectstack's scripts/invoked-as.mjs carries the accurate original (scripts/check-entry-guard.mjs enforces this: a process.argv[1] in an entry-guard position anywhere in scripts/** that is not this module is a failure), which is true in this tree. Nothing here is stale.

Why it is worth recording

objectui#6243 exists because prose ported from this repo described this tree, stayed put while objectui's tree moved underneath it, and ended up telling readers that a rule with a blocking gate behind it was unenforced. The mechanism was a one-way copy with no back-pointer.

⭐ The same one-way shape is still in place for the next port. An agent editing either file here would have no way to learn that a third copy exists and has drifted — the obligation only exists in the copy that is not in front of them.

Cheap fix, not proposed as urgent

A reciprocal pointer in each objectstack copy naming the objectui counterpart, so the pairing is discoverable from any of the three. Needs its own worktree and its own card; ⛔ deliberately not bundled into the objectui fix (objectui PR #6260), which is scoped to one repo and one file.

Refs: objectui#6243 · objectui#6260 · objectui#5984 (the port that brought the prose) · objectui#6092 (closed — the gate and the sweep this drifted against).

Activity

  1. os-steve commented on Aug 25, 2026

    @os-steve
    Collaborator

    Triage: pm:queue (was finding), domain:devx, type Task — cross-domain rider designated.

    Rationale: the sync obligation lives in the invoked-as gate family (scripts/** ⇒ devx); the fix is reciprocal back-pointers so the three-copy obligation is discoverable from all three ends. One of the two target files is packages/cli/src/utils/invocation.ts (cli territory): comment-only, one line — designated by triage to ride the devx PR rather than split; declare the two-file surface in the claim. S, mechanical; sonnet-eligible.

    Constraint: pointers only — both objectstack copies are measured correct in substance; do not "fix" their prose. objectui's copy already names all three ends (objectui PR #6260) — verify its final wording on the merged ref and mirror it.


    Generated by Claude Code

  2. added theissue type on Aug 25, 2026
  3. self-assigned this
    on Aug 25, 2026
  4. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    Claim: PM seat domain:devx @ objectstack, session session_01UjM2ia8Av1v5NqfqQEQmC6, round R2.

    field value
    branch claude/issue-12013-invoked-as-reciprocal-pointers
    worktree ../objectstack-12013 (dedicated, per repo)
    domain domain:devx — landing repo objectstack
    file surface scripts/invoked-as.mjs · packages/cli/src/utils/invocation.ts (2 files, comment-only)
    model tier sonnet — triage graded it sonnet-eligible and I concur
    Clause ② not triggered. Comment-only prose. No contract accept/reject behaviour moves, no public surface widens. dispatch-gates --tier returns no path-derived mandate for either file; the content judgement is the operative one and it is negative.

    Premise re-verified against origin/main = f7b25c546, not against the card body (filed 04:53Z against 497ded780):

    file mentions objectui change the others positive control
    scripts/invoked-as.mjs 0 0 invoked × 15 — file read, grep works
    packages/cli/src/utils/invocation.ts 0 0 186 lines — file exists at the declared length

    So the premise holds unchanged: neither objectstack copy is discoverable from the objectui end.

    Serial constraints. No hot-file collision at claim time. scripts/invoked-as.mjs is not in this seat's serial queue. ⚠️ One adjacency to watch, not a block: #11904 (pm:queue, undispatched) concerns six scripts/** docblocks carrying the over-broad #9465 fence reading — if scripts/invoked-as.mjs turns out to be one of the six, that is a different edit to the same docblock. It is serial behind PR #12030 either way, so this card goes first; #11904's dev will rebase.

    Fold-or-serial: not folded with #11920 (dispatched in the same batch). Gate ① fails — different defect shape (a missing cross-repo back-pointer vs. an absent self-test instrument), different fix, disjoint file surfaces. They run in parallel.

    Cross-repo note. Triage directs mirroring objectui's final wording from PR #6260. That is a read of the objectui tree to copy a sentence; the landing repo is objectstack and every edit is in objectstack. ⛔ No objectui file is touched by this card, and no objectui card is claimed by this seat.


    Generated by Claude Code

  5. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator
    {
      "issue": 12013,
      "status": "done",
      "branch": "claude/issue-12013-invoked-as-reciprocal-pointers",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12084",
      "premise_still_valid": true,
      "summary": "Added a reciprocal pointer to both objectstack copies of the argv[1]-vs-import.meta.url entry-point predicate (scripts/invoked-as.mjs's isEntrypoint/invokedAs, and packages/cli/src/utils/invocation.ts's isProcessEntry), each now naming the other objectstack copy plus objectui's scripts/invoked-as.mjs, mirroring objectui's merged 'All three copies ... Change one, change the others' wording. Comment-only in both files; neither file's existing prose was corrected.",
      "tests": "Final commit 258b2cadf. Build: `pnpm --filter '@objectstack/cli' build` rc0 (docblock-only edit compiles clean); `pnpm --filter '@objectstack/cli' typecheck` rc0; `pnpm --filter '@objectstack/cli' exec vitest run --maxWorkers=2 src/utils/invocation.test.ts` -> Test Files 1 passed, Tests 17 passed. `node scripts/invoked-as.mjs --self-test` -> 11 cases pass (unchanged shape, confirming Zone2#4). `pnpm check:entry-guard` -> 157 scripts/ files, 113 export bindings, 111 inert (unchanged shape). All 23 gates from `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (re-run before and after adding the changeset) green: check:agent-test-spelling, check:changeset-gate-self-tests, check:cross-package-test-inputs, check:entry-guard, check:i18n, check:i18n-coverage, check:objectui-changeset, check:parse-guard, check:pnpm-filter-targets, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check-adr-0087-registration.mjs, check-changeset-no-major.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-empty-changeset.mjs, check-plugin-teardown-shape.mjs, docs-audit/check-affected-docs.mjs, docs-audit/check-drift-comment.mjs, scripts/pm/release-rehearsal-clone.mjs --self-test, plus the standing check:nul-bytes clause. My re-derivation exactly reproduced the PM's 17 pre-changeset + 6 changeset-triggered families, no additions or omissions. One prerequisite unrelated to this diff: check:i18n-coverage needed `pnpm --filter '@objectstack/example-showcase^...' build` first (stale dist/ in a fresh worktree, not a defect); after that it read OK (12 configs, 657 baselined, none new). Control-byte self-scan on both edited files: no matches.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    os-dev-report (reposted — the marker comment above lost its <!-- ... --> wrapper to the body sanitizer; this one opens with the literal fallback text per convention)

    {
      "issue": 12013,
      "status": "done",
      "branch": "claude/issue-12013-invoked-as-reciprocal-pointers",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12084",
      "premise_still_valid": true,
      "summary": "Added a reciprocal pointer to both objectstack copies of the argv[1]-vs-import.meta.url entry-point predicate (scripts/invoked-as.mjs's isEntrypoint/invokedAs, and packages/cli/src/utils/invocation.ts's isProcessEntry), each now naming the other objectstack copy plus objectui's scripts/invoked-as.mjs, mirroring objectui's merged 'All three copies ... Change one, change the others' wording. Comment-only in both files; neither file's existing prose was corrected.",
      "tests": "Final commit 258b2cadf. Build: `pnpm --filter '@objectstack/cli' build` rc0 (docblock-only edit compiles clean); `pnpm --filter '@objectstack/cli' typecheck` rc0; `pnpm --filter '@objectstack/cli' exec vitest run --maxWorkers=2 src/utils/invocation.test.ts` -> Test Files 1 passed, Tests 17 passed. `node scripts/invoked-as.mjs --self-test` -> 11 cases pass (unchanged shape, confirming Zone2#4). `pnpm check:entry-guard` -> 157 scripts/ files, 113 export bindings, 111 inert (unchanged shape). All 23 gates from `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (re-run before and after adding the changeset) green: check:agent-test-spelling, check:changeset-gate-self-tests, check:cross-package-test-inputs, check:entry-guard, check:i18n, check:i18n-coverage, check:objectui-changeset, check:parse-guard, check:pnpm-filter-targets, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check-adr-0087-registration.mjs, check-changeset-no-major.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-empty-changeset.mjs, check-plugin-teardown-shape.mjs, docs-audit/check-affected-docs.mjs, docs-audit/check-drift-comment.mjs, scripts/pm/release-rehearsal-clone.mjs --self-test, plus the standing check:nul-bytes clause. My re-derivation exactly reproduced the PM's 17 pre-changeset + 6 changeset-triggered families, no additions or omissions. One prerequisite unrelated to this diff: check:i18n-coverage needed `pnpm --filter '@objectstack/example-showcase^...' build` first (stale dist/ in a fresh worktree, not a defect); after that it read OK (12 configs, 657 baselined, none new). Control-byte self-scan on both edited files: no matches.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  7. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    ACCEPT (on the work) — PR #12084 · ⛔ landing HELD until CI converges

    Reviewer of record: devx seat, session_01UjM2ia8Av1v5NqfqQEQmC6 (seat #6023).

    ⏳ Not flipping ready yet. 0 failures, but Lint & Repo Gates, Type Check · workspace, Type Check · consumer gates, Type Check · debt ledger, Build Core, three Dogfood legs and two Test Core shards are still in_progress. Same discipline applied to #12080 this round.

    ⭐ The changeset call came out against my leaning, and it was derived rather than argued

    I flagged this as genuinely open and asked for a derivation, not my guess. My leaning was skip-changeset — nothing observable to a consumer moves. That was wrong, and the dev showed why with three readings instead of an opinion:

    1. objectstack has no per-file changeset path analysis. check-empty-changeset.mjs and pr-automation.yml's "Require a changeset" step make a binary author-judgment call; check:changeset-gate-self-tests only pins the checkers' own fixtures. (objectui has check-changeset-presence.mjs for this; objectstack has no equivalent — so the mechanism I was implicitly reasoning from does not exist here.)
    2. The operative rule is the lane doc's own text, tied to file location, not content: 落进已发布包的源 ⇒ 真 changeset. packages/cli is published — I confirmed "name": "@objectstack/cli", "version": "17.2.0", no "private" — and the edit lands in its src/.
    3. The precedent, which I verified myself rather than accepting the citation. .changeset/cli-readme-drop-os-studio.md exists on origin/main and is exactly as described: a patch changeset for a pure prose correction to a README — not even compiled source. (Positive control: 179 changesets in the directory, so the lookup works.) This repo's convention treats "lands in a published package" as sufficient regardless of whether behaviour changes.

    ⇒ Real changeset (@objectstack/cli: patch), not the label. The dev also named the residual honestly: scripts/invoked-as.mjs alone would qualify for skip-changeset, but the rule has no per-file carve-out at PR granularity, so the packages/cli edit governs the whole PR.

    The cross-repo constraint was honoured exactly

    objectui PR #6260 was verified merged (ef2a3bd8d, 2026-08-25T05:20:54Z) and its final wording read directly off objectui:main — not from this card's quotation of it, which is what Zone 2 asked for. Read-only: no objectui file touched, branched, or opened. That is the correct reading of the maintainer's territory ruling — the landing repo is objectstack, and reading a sibling tree to copy a sentence is a read, not a claim.

    It applied the sanitizer lesson unprompted

    The PR body spells one placeholder in words, with the reason stated inline: "GitHub's body sanitizer strips a literal angle-bracket fragment even inside a code span." That is the rule this seat adopted mid-shift after damaging its own evidence twice, arrived at independently here.

    Scope held

    Comment-only in both files; neither file's existing prose corrected, per the triage constraint — both were already accurate for this tree. 28 additions, 3 deletions across 3 files (two source, one changeset). scripts/invoked-as.mjs --self-test still 11 cases, check:entry-guard still 157 files / 113 bindings / 111 inert — the docblock-only assumption confirmed by measurement rather than assumed.

    23 gate families derived in the dev's own worktree (not the shared checkout), reproducing my 17 pre-changeset + 6 changeset-triggered lists exactly, all green. One declared prerequisite unrelated to the diff: check:i18n-coverage needed a dependency build first because of a stale dist/ in a fresh worktree — declared as not-a-defect rather than silently worked around.

    Next step: flip ready, arm SQUASH, verify by the queue ref, once every named gate job reports completed green.


    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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions