fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(pm): split the governed-merges attribution column three ways - #12658

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way
Aug 27, 2026
Merged

fix(pm): split the governed-merges attribution column three ways#12658
os-litant merged 1 commit into
mainfrom
claude/issue-12645-attribution-three-way

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12645

The attribution column was picked on entry.attribution alone, so it covered
three facts with two words. The entry that fell through the gap is the loudest
line the sweep prints: a mainline commit whose subject names no PR. main()
skips it deliberately — if (entry.pr == null) continue; — because there is no
pull request to query, so zero channels were tried, and the column said
every one of them had failed. It also referred the reader to "the attribution
note below", which summariseAttributionFailures never produces for such an
entry: that function groups only entries carrying attributionError, and this
one carries none.

Reproduction — the card's constructed sweep, before and after

A governed PR-less mainline entry, zero API lookups, rendered through
renderReport (no network, no checkout). The 0 API lookup(s) line is printed
from the same report, four lines above the entry.

Before (de4733663, current origin/main at branch point):

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by UNAVAILABLE — every channel failed; see the attribution note below

After:

 scanned 3 mainline commit(s); 0 API lookup(s).
• objectstack-ai/objectstack ⚠️ NO PR NUMBER IN SUBJECT — direct push to main? investigate — docs(adr): kernel object ownership
commit b2d8707f6 @ 2026-08-14T05:40:28+00:00; merged_by NOT LOOKED UP — no PR number in the subject, so there is no pull request to query (not a channel failure)

The change

A new pure exported helper, attributionCell(entry), following this file's
established shape (describeWindow, windowEdgeReason, nextRoundRefLine
one pure fragment per report region, so --self-test asserts on the words):

  1. resolved — a channel answered; the cell names which one. Unchanged.
  2. UNAVAILABLE, every channel failedattributionError is present, so
    channels really were tried. This is now the only branch that points at
    the attribution note, because it is the only one the note is written for.
  3. NOT LOOKED UP — no reading was attempted, with the reason read off the
    entry rather than assumed
    . An absent PR number is the case main()
    produces; an entry that has a PR number yet reaches this branch gets an
    honest residual instead of being told it has no PR number. Asserting an
    untried channel and asserting an absent PR number are the same defect in
    different words, and fixing one by introducing the other would not be a fix.

Report-only, as ruled. No judgment moves: attributionFailed — and with it
the INCOMPLETE exit — is still set by a real channel failure alone, so a PR-less
entry still does not make the sweep incomplete on attribution grounds. The
--json path emits raw entries and is untouched. The direct-push warning is
not weakened; the header block that states a PR-less commit is more
anomalous than any PR merge now also records why its column reads NOT LOOKED UP.

Assertions added (13; --self-test 193 → 206, all green)

Placed beside the two existing fixtures, since the defect was a two-way split
covering three facts and the set is what pins it:

  • a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
  • and-it-says-WHY-nothing-was-queried
  • a-not-looked-up-entry-points-at-no-attribution-note
  • and-the-report-prints-none-for-itsummariseAttributionFailures really
    returns nothing for the entry, so the dangling pointer is pinned from both ends
  • the-note-pointer-belongs-to-the-every-channel-failed-case-alone
  • the-third-case-does-not-soften-the-direct-push-warning
  • and-carries-no-resolved-column-caveat
  • cell-case-1-resolved-names-its-channel
  • cell-case-2-every-channel-failed-needs-an-attributionError
  • cell-case-3-no-pr-number-is-nothing-to-query
  • cell-residual-never-invents-a-missing-pr-number
  • and-the-residual-is-not-a-channel-failure-either
  • a-resolved-reading-outranks-a-stale-error

Verification

Everything below ran at 8c5717fb5, the final commit on this branch, under
the shared verify lock.

node scripts/pm/check-governed-merges.mjs --self-test — its own printed
verdict line:

✓ check-governed-merges --self-test: 206 assertions (…the three-way attribution
column (resolved · every-channel-failed · NOT LOOKED UP, and the note pointer
that belongs to the middle one alone)…)
live: the real generator declared 9 output(s) and certified this tree

Reverse verification (expected direction: RED). The fix was committed first,
so the restore leg has a real reference point. The mutation reverted the call
site to the pre-fix two-way ternary; it was proved on disk before anything
was read — deleted-text count 0, injected-text count 1, and the on-disk blob
a7e619a8… differing from the HEAD blob 0b534cd4…. The mutated tree failed
with exactly the three rendering pins that route through the call site:

✗ check-governed-merges --self-test — 3 failure(s)
• a-pr-less-entry-is-NOT-LOOKED-UP-not-a-failed-lookup
• and-it-says-WHY-nothing-was-queried
• a-not-looked-up-entry-points-at-no-attribution-note

The ten attributionCell pins stayed green under that mutation, correctly: the
mutation removed the call, not the helper, so those pins were not exercised by
it. Restore was proved by state rather than by exit code — on-disk blob back to
0b534cd4… and git diff HEAD empty — and the restored tree re-ran green at
206. The script carried a trap … EXIT INT TERM with absolute paths throughout.
No build or dist/ resolution is involved: this module is executed directly by
node from source.

Derived gate unionnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (paths taken by the script from the merge base, not
hand-supplied), giving 11 path-matched families plus the 2 convention-triggered
by a gate-script edit. Run with importer siblings (three modules import from
this file) and the universal control-byte gate; 17 of 17 green:

check:agent-test-spelling · check:bash32-floor · check:cli-command-ids
check:cross-package-test-inputs · check:entry-guard · check:parse-guard
check:pm-governed-merges · check:pnpm-filter-targets · check-ci-filter-parity
check-cross-package-test-inputs · check-skills-token-ratchet
bare-root-worklist --self-test · check:pm-dispatch-gates · check:nul-bytes
check:pm-governed-prose · check-governed-queue-guard --self-test
ci-failure --self-test

check:pm-dispatch-gates printed ✓ dispatch-gates self-test: 719 cases pass.
and check:nul-bytes printed check-nul-bytes: OK (scanned 7018 text file(s) … no raw ASCII control bytes); a separate control-byte scan over the edited file
found no hits.

No changeset. This publishes nothing — internal PM tooling, and a pure bug
fix, which AGENTS.md exempts. It is also this file's own convention: none of the
last five commits touching it carried one.


Generated by Claude Code

A PR-less mainline entry — the loudest line the sweep prints — rendered
"merged_by UNAVAILABLE — every channel failed; see the attribution note
below" when zero channels had been tried: `main()` skips an entry with no
PR number (there is no pull request to query), and the column was picked
on `entry.attribution` alone. The note it pointed at is never produced for
such an entry either, since `summariseAttributionFailures` groups only
entries carrying `attributionError`.
The column now reads: resolved · UNAVAILABLE, every channel failed (an
`attributionError` is present — the only case that may point at the note)
· NOT LOOKED UP, with the reason read off the entry rather than assumed.
Report-only: no judgment moves. `attributionFailed` and the INCOMPLETE
exit are still driven by real channel failures alone, and the direct-push
warning on a PR-less entry is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-litant@claude