docs: say the fork gate guards only the review credentials #33

Merged
jercik merged 4 commits from docs/fork-gate-boundary into main 2026-10-05 09:31:24 +00:00
Owner

Corrects the Security model bullet flagged in the review of #32 (claim 01M44DH0VFHYT6MWYWH96G8Z8Y): FORGEJO_TOKEN is read before the fork gate on every path, and only reviewCredentials() waits for it.

Two code comments made the same error and get the same correction: the header of src/wrapper/event-context.ts and the isFork doc comment in src/contract/types.ts.

🤖 Generated with Claude Code

Corrects the Security model bullet flagged in the [review of #32](https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32#issuecomment-122860) (claim `01M44DH0VFHYT6MWYWH96G8Z8Y`): `FORGEJO_TOKEN` is read before the fork gate on every path, and only `reviewCredentials()` waits for it. Two code comments made the same error and get the same correction: the header of `src/wrapper/event-context.ts` and the `isFork` doc comment in `src/contract/types.ts`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs: say the fork gate guards only the review credentials
Some checks failed
commit-msg / commitlint (pull_request) Successful in 17s
Checks / quality-checks (pull_request) Successful in 41s
Review / Review (pull_request_target) Has been cancelled
261dd940ff
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
docs: say the fork verdict precedes only reviewCredentials()
Some checks failed
commit-msg / commitlint (pull_request) Successful in 25s
Checks / quality-checks (pull_request) Successful in 50s
Review / Review (pull_request_target) Has been cancelled
354462be1e
The event-context.ts header claimed the orchestrator evaluates the fork
verdict before any credential module is called. forgeCredentials() runs
first, so only reviewCredentials() is gated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
docs: say isFork is evaluated before reviewCredentials()
All checks were successful
commit-msg / commitlint (pull_request) Successful in 24s
Checks / quality-checks (pull_request) Successful in 52s
Review / Review (pull_request_target) Successful in 11m39s
a4e7bd0f07
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Review 01M44FMS560BRRJR823QSZRW2E — head 67a520252f78dec2bcfb96460a998604e04b241d

Review — j4k-oss/review-wrapper @ 209cb1127f

Scope: diff against base tree e69943819640
Status: dispatched — coverage complete (5/5 slots terminal)
Facts: current review-wide projection

Computed under:

{
  "abandonment": "abandonment-v1",
  "anchor_recipe": 1,
  "batch_policy": "batch-v1",
  "coverage": "coverage-v3",
  "dispatch_policy": "dispatch-v2",
  "grounder_version": 1,
  "grounding_read_rule": "grounding-read-v1",
  "promotion_policy": "promotion-v1",
  "report": "report-v4",
  "tally": "tally-v1",
  "triage_settle": "triage-settle-v2"
}

Findings (8)

medium — The action input table duplicates the input declared in action.yml

  • claim: 01M44FWM69QYH21FMRAYX73KZD
  • anchor: README.md (snippet)
## Action inputs

| Input                     | Meaning                                                                                                                                                                                                                   |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `expected-service-origin` | Origin `REVIEW_SERVICE_URL` must match exactly, e.g. `https://review.j4k.dev`. Left unset — or set to the empty string an undefined `${{ vars.X }}` expands to — the pin is off and only the `https:` scheme is enforced. |
  • lens: writing-quality · arm: default
  • verdicts: 2 valid / 0 invalid / 0 uncertain
    • pass 01M44G2XZ0AJBP8KMP9MDVW8X9 · valid: The exact README table names expected-service-origin as an action input. The reviewer reports that the runner-read action.yml inputs mapping defines that same sole member and its origin-pin behavior. The table is therefore a second editable copy of the input contract, even though the members currently agree. Point to action.yml and retain the caveat in its input description. Historical matching verdicts have no concrete refutation.
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README table lists expected-service-origin and its behavior. The reviewer reports that runner-read action.yml defines that same sole input and description. The second catalog can silently stale even though it agrees today. Point to action.yml for the input contract and retain any distinct security rationale. Historical matches offer no concrete refutation.
  • duplicates: 01M44HVZRQB3CNWKBEZS6Y25JB (writing-quality)
  • disposition: none

The README gives readers a second definition of the action's inputs. When an input or its behavior changes in the runner-facing action.yml, this table can silently give callers an incomplete or wrong contract.

The table lists expected-service-origin and repeats its origin-pin description. action.yml is the source the runner reads and declares that same sole input; the source members and copied members are both {expected-service-origin}, with no difference at this revision.

Replace the table with a pointer to action.yml and keep the origin-pin caveat in that input's description there. This preserves the operational guidance while leaving one place to update the set.

I compared the README table with the complete inputs mapping in action.yml and checked the action's environment read in reviewCredentials() in src/credentials.ts. The copy still agrees; the defect is the independent maintenance obligation. A runner that read the README as its input specification would refute that premise, but the reviewed action.yml is the runner's declaration.

medium — The outcome table restates the outcomes and exits defined in code

  • claim: 01M44FXC8EWSST2AF4EADZXNJX
  • anchor: README.md (snippet)
## Outcomes and exit codes

| Row | Outcome               | Behaviour                                                                                                                                                     | Exit |
| --- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| 1   | `fork-skip`           | Explicit "not reviewed" block; no `/v1` call precedes it.                                                                                                     | 0    |
| 2   | `service-unreachable` | No comment — there is nothing to reconcile.                                                                                                                   | 1    |
| 3   | `auth-failed`         | Names the `REVIEW_CAPABILITY_TOKEN` rotation.                                                                                                                 | 1    |
| 4   | `forge-host-mismatch` | Quotes the service's problem detail.                                                                                                                          | 1    |
| 5   | `ask-conflict`        | Names the two real causes of an ask-key conflict.                                                                                                             | 1    |
| 6   | `stuck`               | Reconciles what stands, then fails (includes a stalled triage). Job log: `review did not settle (<kind>):`.                                                   | 1    |
| 7   | `partial-coverage`    | Full reconcile, then fails. Names the lossy slots and a dispatch re-ask from `main`. Job log: `review wrapper (partial-coverage):`.                           | 1    |
| 8   | `grounding-pending`   | Full reconcile, then fails.                                                                                                                                   | 1    |
| 9   | `clean-zero-findings` | Full reconcile with an explicit zero block.                                                                                                                   | 0    |
| 10  | `clean-findings`      | Full reconcile.                                                                                                                                               | 0    |
| 11  | superseded            | A flag, not an outcome; it does not itself change the exit code. Coverage and report accounting stay pinned; triage, claims, and findings remain review-wide. | —    |
| 12  | `head-moved`          | No PR comment or conversation-resolution writes; the newer run owns the comment surface. See the timing below.                                                | 0    |
  • lens: writing-quality · arm: default
  • verdicts: 2 valid / 0 invalid / 0 uncertain
    • pass 01M44G2XZ0AJBP8KMP9MDVW8X9 · valid: The exact README table enumerates outcomes and exit codes. The reviewer lists the same outcome members in OUTCOMES and reports the matching OUTCOME_EXIT mapping in src/contract/types.ts. Superseded is expressly labeled a flag and is not an extra outcome. This handwritten table copies code-defined membership and exits; point to the definitions and preserve distinct recovery guidance without an exhaustive roster. No current mismatch is reported, so medium severity fits.
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README table enumerates outcomes and exits. The reviewer lists the same eleven outcomes in OUTCOMES and the corresponding exits in OUTCOME_EXIT at src/contract/types.ts; superseded is expressly a flag, not an extra outcome. This is a second editable set and exit map. Point to those definitions and preserve distinct operational guidance without an exhaustive roster or row numbering. No current mismatch is established, so medium fits.
  • duplicates: 01M44HSEXSK05FNT7J4KM79MSA (writing-quality)
  • disposition: none

The README's outcome table creates a second list of supported outcomes and their exit codes. A new or changed outcome can leave this user-facing table silently stale even while the wrapper returns the correct code.

The source is OUTCOMES and OUTCOME_EXIT in src/contract/types.ts. Source members: fork-skip, service-unreachable, auth-failed, forge-host-mismatch, ask-conflict, stuck, partial-coverage, grounding-pending, clean-zero-findings, clean-findings, head-moved. Copied outcome members: fork-skip, service-unreachable, auth-failed, forge-host-mismatch, ask-conflict, stuck, partial-coverage, grounding-pending, clean-zero-findings, clean-findings, head-moved. No member appears in only one list, and the exits agree today. The table also includes superseded, explicitly marked as a flag rather than an outcome; WaitResult.superseded defines that flag.

Point readers to those definitions for the membership and exit mapping. Keep the non-obvious recovery and timing guidance as focused prose near the existing error and rerun sections, without a complete outcome roster or row count. This preserves what users need to do while removing the duplicate set.

I compared the table with OUTCOMES, OUTCOME_EXIT, WaitResult, and runWrapper() in src/wrapper/orchestrator.ts; this is a static comparison, not a runtime probe. Generation of this README table from the code would refute the maintenance risk, but the reviewed tree shows a handwritten Markdown table.

medium — The composition paragraph copies the reconciler count and members

  • claim: 01M44FXXFPF39NW64Q3QVSFQM0
  • anchor: README.md (snippet)
`src/main.ts` is the only place the modules meet. It reads the forge credentials and
`GITHUB_REPOSITORY`, builds the forge client, derives the run's inputs from the event
payload, and hands the orchestrator its collaborators: the wait protocol, the clock, and
the three reconcilers (summary, inline, dispositions). The orchestrator's exit code
becomes the process exit code, so the job's conclusion is the outcome table above.
  • lens: writing-quality · arm: default
  • verdicts: 2 valid / 0 invalid / 0 uncertain
    • pass 01M44G2XZ0AJBP8KMP9MDVW8X9 · valid: The exact README paragraph counts and names summary, inline, and dispositions as the three reconcilers. The reviewer reports the same members in WrapperDeps in src/contract/types.ts and the wiring in src/main.ts. That is a complete copy of the code-defined collaborator set with no current mismatch. Refer to WrapperDeps while keeping the useful wiring and process-exit explanation. Historical matching verdicts supply no refutation.
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README paragraph counts and names summary, inline, and dispositions as the three reconcilers. The reviewer reports that WrapperDeps in src/contract/types.ts defines precisely those collaborators and src/main.ts supplies them. The count and member list copy a code-defined set that can drift. Refer to WrapperDeps while retaining the wiring and process-exit explanation. No current member mismatch is reported.
  • duplicates: 01M44HTPWS8WS1WGTC6R50VBKQ (writing-quality)
  • disposition: none

The composition paragraph gives readers a second definition of how many reconcilers the wrapper wires. Adding or removing one in WrapperDeps or src/main.ts can leave the count and parenthesized names wrong without affecting the build.

WrapperDeps defines the injected reconciler collaborators as summary, inline, and dispositions; src/main.ts supplies the same members. The README copy says three reconcilers (summary, inline, dispositions). Both sets have those three members and none appears in only one set today.

Replace the count and list with a pointer to WrapperDeps, such as saying that src/main.ts supplies the orchestrator's collaborators defined there. The paragraph can keep the distinct explanation that the orchestrator's exit code becomes the process exit code.

I compared this paragraph with WrapperDeps in src/contract/types.ts and the dependency object passed to runWrapper() in src/main.ts; this was a static source comparison. The count would cease to be a duplicate only if this prose were the source consumed to build that dependency contract, which the reviewed code does not show.

medium — The recovery table restates the remedies already emitted in job logs

  • claim: 01M44HT0GJRH67A2T0S35D09N0
  • anchor: README.md (snippet)
| Message                                       | Recovery                                                                                                               |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `provider session limit reached`              | Wait for the session limit to reset or ask the operator to lift it.                                                    |
| `selected model at capacity`                  | Wait for the model to accept new work.                                                                                 |
| `agent execution failed`                      | Ask the operator to inspect the failed agent logs and correct the reported error.                                      |
| `sandbox infrastructure failed`               | Ask the operator to check sandbox provisioning, controller connectivity, and resources.                                |
| `execution failed for an unrecognized reason` | Copy the quoted code from `(code "…")` on that log line and ask the operator to look it up in the review service logs. |
  • lens: writing-quality · arm: default
  • verdicts: 1 valid / 0 invalid / 0 uncertain
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README table gives a complete inventory of recovery messages and actions. The reviewer identifies four selected codes plus an unknown fallback in src/wrapper/remedies.ts, and reports that formatExecutionRecovery already emits the corresponding remedies in job logs; the bundled @j4k/review formatter supplies the labels. The copied set can stale even though no difference is reported now. Direct operators to the emitted recovery text and maintainers to the formatter.
  • disposition: none

The recovery table is a second inventory of execution-failure cases and remedies, so a new or changed failure mapping can leave the README giving stale instructions. src/wrapper/remedies.ts defines the selected codes provider-session-limit, provider-capacity, agent-execution-failed, and infrastructure-failure, plus an unknown-code fallback; the bundled @j4k/review formatter supplies their displayed message labels. The table covers those four cases and the fallback, with no member present on only one side at this revision. Replace the table with a direction to follow the recovery text on the job log line and a pointer to formatExecutionRecovery for maintainers. That keeps the operator action and quoted-code fallback available where they are actually produced without copying the set. I compared the table with executionRemedies and formatExecutionRecovery in src/wrapper/remedies.ts and with the bundled formatExecutionFailure mapping in dist/index.mjs; this is a source comparison, not an execution test. A newly mapped code or remedy change without a table edit would establish the predicted drift; the copy currently agrees.

medium — The environment contract copies the required-variable inventory

  • claim: 01M44HVQDW8RY9BSA3AD87VP0C
  • anchor: README.md (snippet)
## Environment contract

| Variable                             | Meaning                                                                                                                                        |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `REVIEW_SERVICE_URL`                 | Base URL of the review service (an org-managed variable). Must parse as an `https:` URL and must not end with `/v1`, which the client appends. |
| `REVIEW_CAPABILITY_TOKEN`            | Capability token for the service's `/v1` API (an org secret).                                                                                  |
| `FORGEJO_TOKEN`                      | The job's own task token, used for every forge write.                                                                                          |
| `GITHUB_API_URL`                     | Forge API base, supplied by the runner.                                                                                                        |
| `GITHUB_EVENT_*`, `GITHUB_RUN_ID`, … | Standard runner event environment; the wrapper's PR inputs come from the event payload.                                                        |
| `GITHUB_RUN_ATTEMPT`                 | Forgejo run attempt (1-based). Required on every path. The wrapper re-triages stalled or failed triage only when this is ≥ 2.                  |

The forge and runner variables — `FORGEJO_TOKEN`, `GITHUB_API_URL`, `GITHUB_EVENT_*`,
`GITHUB_RUN_ID`, `GITHUB_RUN_ATTEMPT`, `GITHUB_REPOSITORY`, `GITHUB_SERVER_URL` — are
required on every path.
`REVIEW_SERVICE_URL` and `REVIEW_CAPABILITY_TOKEN` are required only on the
same-repository path: a fork pull request returns at the fork gate without opening a
service session, so neither is ever read. Where a variable is required, a missing one is
a configuration defect and fails the run immediately rather than sending an
empty-string request.
  • lens: writing-quality · arm: default
  • verdicts: 1 valid / 0 invalid / 0 uncertain
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README passage presents a required-variable inventory. The reviewer lists the requireEnv inputs in readWrapperInputs, forgeCredentials, and reviewCredentials and reports that the README covers those names, using GITHUB_EVENT_* shorthand, with no established missing member. It is a separately maintained copy that can silently stale. Point to the source functions for membership, while keeping the fork-path requirement and immediate-failure rule as non-inventory guidance. Medium fits absent a proved current mismatch.
  • disposition: none

The table and following sentence maintain a second list of variables the wrapper requires, so a new required read can leave setup documentation silently incomplete. In the source, readWrapperInputs requires GITHUB_EVENT_NAME, GITHUB_EVENT_PATH, GITHUB_REPOSITORY, GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT, and GITHUB_SERVER_URL; forgeCredentials requires GITHUB_API_URL and FORGEJO_TOKEN; reviewCredentials requires REVIEW_SERVICE_URL and REVIEW_CAPABILITY_TOKEN after the fork gate. The README covers those ten through explicit names and the GITHUB_EVENT_* shorthand; no known member appears only on one side, though the shorthand does not tell a reader which event fields are read. Point to these functions for the required names, while keeping the conditional fork-gate rule and immediate missing-value failure as prose; keep org-secret and runner provenance beside the corresponding definitions if needed. This preserves the configuration decisions without another variable set. I compared this section with src/main.ts, src/wrapper/event-context.ts, and src/credentials.ts; no runtime check was needed for the inventory. A new requireEnv call with no README update would establish the stale-copy failure, while an environment name available to readers only here would refute the proposed deletion; none appears in this tree.

medium — The WrapperInputs header falsely says its fields are never computed

  • claim: 01M44HRFXHK0QP5RCK8NKVCP1J
  • anchor: src/contract/types.ts (snippet)
// ---- Inputs (contract §2: derived from the event, never computed) ----
  • lens: writing-quality · arm: default
  • verdicts: 1 valid / 0 invalid / 0 uncertain
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact header claims the inputs are never computed. The reviewer reports that readWrapperInputs computes isFork, normalizes the forge host, parses the attempt, and constructs reTriageAskKey. These concrete operations contradict the blanket wording and could mislead a maintainer about the contract. Remove the false phrase while retaining a useful contract pointer; the unavailable external section does not undo the observed conflict in the subject code.
  • disposition: none

The header can lead a maintainer to treat local derivation as a contract violation, even though the interface requires derived values. readWrapperInputs calculates isFork by comparing repository names, lowercases the parsed forge host, parses the run attempt, and constructs reTriageAskKey; those values are not simply fields read from the event. Replace the header with a current description such as “Wrapper inputs from the runner and forge, including locally derived values,” preserving the contract pointer without the false restriction. I read this interface and both event branches in src/wrapper/event-context.ts; this is a source trace, not a runtime test. The external contract cited as §2 is unavailable in this snapshot, so its intended wording cannot be verified here; the subject code itself establishes that the blanket “never computed” statement is false.

medium — Input derivation comment duplicates the two PR metadata sources

  • claim: 01M44FWJB8RYQ4ZV29426J4CPN
  • anchor: src/wrapper/event-context.ts (snippet)
// Input derivation (roadmap 1.2, contract §2). PR metadata is read from the
// event payload or fetched from the forge. Merge base, digest and scope
  • lens: restated-sets · arm: default
  • verdicts: 2 valid / 0 invalid / 0 uncertain
    • pass 01M44G2XZ0AJBP8KMP9MDVW8X9 · valid: The exact comment presents event payload and forge as the complete PR metadata source set. The reviewer identifies the two readWrapperInputs branches in src/wrapper/event-context.ts, with readEventPayload and forge.getPull defining those same members. This adjacent comment copies the code-defined set and can drift; delete its source-list sentence. No current member mismatch is reported, so medium severity fits.
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact adjacent comment says PR metadata comes from the event payload or forge. The reviewer identifies readEventPayload and forge.getPull branches in src/wrapper/event-context.ts as the code-defined two-source set, with no current difference. This comment restates that set and can drift; delete the source-list sentence while keeping distinct intent. Medium severity fits.
  • disposition: none

The opening comment gives a complete account of where PR metadata comes from. A later change to either input path would need to update this second account, or maintainers could rely on a stale description while reviewing the fork gate.

In readWrapperInputs, the pull_request_target branch gets head, base, and pull-number fields from readEventPayload(eventPath), while the workflow_dispatch branch gets them from forge.getPull(prNumber). The source members are event payload and forge; the comment copies event payload and forge. No member occurs in only one list, so the copy currently agrees.

Delete the source-list sentence from this comment. The two branches immediately below it define the sources, and the rest of the comment can retain its distinct explanation of work delegated to the service and the fork gate.

I read the full src/wrapper/event-context.ts and traced both branches of readWrapperInputs, then checked src/wrapper/event-context.test.ts for their exercised paths. This is static reasoning, not a runtime reproduction. The comment is neither an input the system reads nor a dated record or example; readers can inspect the adjacent source. The branch bodies are the decisive evidence for the two current members. A rule fixing those sources independently of this code would refute the maintenance-risk premise, but none appears in the subject.

low — README security paragraph duplicates internal credential call order and test coverage

  • claim: 01M44FWA4NTNW284GH2SQT810M
  • anchor: README.md (snippet)
- Forgejo does pass secrets to fork `pull_request_target` runs, so the real boundary is
  code-path ordering: `readWrapperInputs` decides whether the pull request is a fork
  **before** `reviewCredentials()` is called. `forgeCredentials()`, which reads the forge
  task token, runs before the gate on every path. A fork run returns at the gate, so
  nothing `reviewCredentials()` reads is read on that path. The isolation test in
  `src/credentials.test.ts` checks which modules import the credentials module, and an
  orchestrator unit test checks that a fork run never opens a review session.
  • lens: project-docs · arm: default
  • verdicts: 2 valid / 0 invalid / 0 uncertain
    • pass 01M44G2XZ0AJBP8KMP9MDVW8X9 · valid: The exact README excerpt documents internal credential call order and two test checks. The reviewer reports that this passage was changed in the diff and traces forgeCredentials, readWrapperInputs, the fork return, and the deferred openSession call. Those details are implementation and test inventory in a human onboarding README; the project-docs rule puts how it works in code. Keep the user-facing fork guarantee as a short security summary. Low severity fits a wrong-home finding.
    • pass 01M44HX1VSNH8MET4Q0243HBNG · valid: The exact README excerpt narrates credential helper order and names two tests. The reviewer reports this passage was edited and traces forgeCredentials before readWrapperInputs, then the fork return before the deferred reviewCredentials call. That is internal implementation and test inventory in a human-onboarding README; the project-docs skill assigns how-it-works detail to code and tests. Keep the user-facing fork credential guarantee as a short summary. Low severity fits the wrong-home finding.
  • duplicates: 01M44HVDF2E0TB2KVFQ1YWN547 (project-docs)
  • disposition: none

The edited security paragraph makes the README a second source for internal call order and test inventory. That creates a concrete maintenance cost for action users: this edit already corrects the earlier claim that the fork gate ran before any credential module was called, although the unchanged entry point calls forgeCredentials() first. A later refactor can again leave readers with a false account of which token is read on fork runs.

The changed passage says readWrapperInputs decides the fork before reviewCredentials() runs, that forgeCredentials() runs first, and that two named tests check module imports and session opening. In src/main.ts, the forge initializer calls forgeCredentials(), then awaits readWrapperInputs(forge), and supplies reviewCredentials() only inside the openSession thunk. In src/wrapper/orchestrator.ts, runWrapper returns from reconcileForkSkip before calling review, where deps.openSession() occurs. The previous README wording said the fork gate was evaluated before the credentials module was ever called; the diff changes that wording without changing this call path.

Keep the user-facing guarantee in README in one sentence: fork runs do not open a review session or read the review capability token. Remove the function-by-function sequence and test inventory from this passage; their homes are the existing source and tests. The README exception in the project-docs skill covers human onboarding and a short security summary, while this call sequence and test inventory restate implementation rather than explain a user-facing choice or a trade-off.

I read the complete README, the changed src/contract/types.ts and src/wrapper/event-context.ts, src/main.ts, src/credentials.ts, src/wrapper/orchestrator.ts, and the credential and fork-gate tests. This is a static source trace; I did not run the action. No CONTEXT.md, CONTEXT-MAP.md, or ADR exists in the tree. An explicit repository documentation convention requiring the README to carry this internal security call trace would refute the placement finding; I found none in README or AGENTS.md.

Other claims

  • grounding-pending (0)
  • ungrounded (0)
  • rejected (0)
  • duplicate-of (4)
    • 01M44HSEXSK05FNT7J4KM79MSA medium — The outcomes table duplicates the code-defined outcome set → 01M44FXC8EWSST2AF4EADZXNJX
    • 01M44HTPWS8WS1WGTC6R50VBKQ medium — The composition paragraph copies the reconciler set from main.ts → 01M44FXXFPF39NW64Q3QVSFQM0
    • 01M44HVDF2E0TB2KVFQ1YWN547 low — README security model duplicates the internal credential call sequence → 01M44FWA4NTNW284GH2SQT810M
    • 01M44HVZRQB3CNWKBEZS6Y25JB medium — The Action inputs table repeats action.yml → 01M44FWM69QYH21FMRAYX73KZD
  • unadjudicated (0)

Coverage

Coverage pass: 01M44HJR3QV5JN9PKQ4J2X163F
Accounting: complete
Slot health: healthy

lens part arm unit status runs loss
general-bug whole default no-claims 1 no
writing-quality whole default claims-emitted 1 no
test-trimming whole default no-claims 1 no
restated-sets whole default no-claims 1 no
project-docs whole default claims-emitted 1 no
<!-- review:summary --> **Review** `01M44FMS560BRRJR823QSZRW2E` — head `67a520252f78dec2bcfb96460a998604e04b241d` # Review — j4k-oss/review-wrapper @ 209cb1127ffb Scope: diff against base tree `e69943819640` Status: dispatched — coverage complete (5/5 slots terminal) Facts: current review-wide projection Computed under: ```json { "abandonment": "abandonment-v1", "anchor_recipe": 1, "batch_policy": "batch-v1", "coverage": "coverage-v3", "dispatch_policy": "dispatch-v2", "grounder_version": 1, "grounding_read_rule": "grounding-read-v1", "promotion_policy": "promotion-v1", "report": "report-v4", "tally": "tally-v1", "triage_settle": "triage-settle-v2" } ``` ## Findings (8) ### medium — The action input table duplicates the input declared in action.yml - claim: `01M44FWM69QYH21FMRAYX73KZD` - anchor: `README.md` (snippet) ``` ## Action inputs | Input | Meaning | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `expected-service-origin` | Origin `REVIEW_SERVICE_URL` must match exactly, e.g. `https://review.j4k.dev`. Left unset — or set to the empty string an undefined `${{ vars.X }}` expands to — the pin is off and only the `https:` scheme is enforced. | ``` - lens: writing-quality · arm: default - verdicts: 2 valid / 0 invalid / 0 uncertain - pass `01M44G2XZ0AJBP8KMP9MDVW8X9` · valid: The exact README table names expected-service-origin as an action input. The reviewer reports that the runner-read action.yml inputs mapping defines that same sole member and its origin-pin behavior. The table is therefore a second editable copy of the input contract, even though the members currently agree. Point to action.yml and retain the caveat in its input description. Historical matching verdicts have no concrete refutation. - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README table lists expected-service-origin and its behavior. The reviewer reports that runner-read action.yml defines that same sole input and description. The second catalog can silently stale even though it agrees today. Point to action.yml for the input contract and retain any distinct security rationale. Historical matches offer no concrete refutation. - duplicates: `01M44HVZRQB3CNWKBEZS6Y25JB` (writing-quality) - disposition: none > The README gives readers a second definition of the action's inputs. When an input or its behavior changes in the runner-facing action.yml, this table can silently give callers an incomplete or wrong contract. > > The table lists `expected-service-origin` and repeats its origin-pin description. `action.yml` is the source the runner reads and declares that same sole input; the source members and copied members are both {`expected-service-origin`}, with no difference at this revision. > > Replace the table with a pointer to `action.yml` and keep the origin-pin caveat in that input's description there. This preserves the operational guidance while leaving one place to update the set. > > I compared the README table with the complete `inputs` mapping in `action.yml` and checked the action's environment read in `reviewCredentials()` in `src/credentials.ts`. The copy still agrees; the defect is the independent maintenance obligation. A runner that read the README as its input specification would refute that premise, but the reviewed `action.yml` is the runner's declaration. ### medium — The outcome table restates the outcomes and exits defined in code - claim: `01M44FXC8EWSST2AF4EADZXNJX` - anchor: `README.md` (snippet) ``` ## Outcomes and exit codes | Row | Outcome | Behaviour | Exit | | --- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | | 1 | `fork-skip` | Explicit "not reviewed" block; no `/v1` call precedes it. | 0 | | 2 | `service-unreachable` | No comment — there is nothing to reconcile. | 1 | | 3 | `auth-failed` | Names the `REVIEW_CAPABILITY_TOKEN` rotation. | 1 | | 4 | `forge-host-mismatch` | Quotes the service's problem detail. | 1 | | 5 | `ask-conflict` | Names the two real causes of an ask-key conflict. | 1 | | 6 | `stuck` | Reconciles what stands, then fails (includes a stalled triage). Job log: `review did not settle (<kind>):`. | 1 | | 7 | `partial-coverage` | Full reconcile, then fails. Names the lossy slots and a dispatch re-ask from `main`. Job log: `review wrapper (partial-coverage):`. | 1 | | 8 | `grounding-pending` | Full reconcile, then fails. | 1 | | 9 | `clean-zero-findings` | Full reconcile with an explicit zero block. | 0 | | 10 | `clean-findings` | Full reconcile. | 0 | | 11 | superseded | A flag, not an outcome; it does not itself change the exit code. Coverage and report accounting stay pinned; triage, claims, and findings remain review-wide. | — | | 12 | `head-moved` | No PR comment or conversation-resolution writes; the newer run owns the comment surface. See the timing below. | 0 | ``` - lens: writing-quality · arm: default - verdicts: 2 valid / 0 invalid / 0 uncertain - pass `01M44G2XZ0AJBP8KMP9MDVW8X9` · valid: The exact README table enumerates outcomes and exit codes. The reviewer lists the same outcome members in OUTCOMES and reports the matching OUTCOME_EXIT mapping in src/contract/types.ts. Superseded is expressly labeled a flag and is not an extra outcome. This handwritten table copies code-defined membership and exits; point to the definitions and preserve distinct recovery guidance without an exhaustive roster. No current mismatch is reported, so medium severity fits. - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README table enumerates outcomes and exits. The reviewer lists the same eleven outcomes in OUTCOMES and the corresponding exits in OUTCOME_EXIT at src/contract/types.ts; superseded is expressly a flag, not an extra outcome. This is a second editable set and exit map. Point to those definitions and preserve distinct operational guidance without an exhaustive roster or row numbering. No current mismatch is established, so medium fits. - duplicates: `01M44HSEXSK05FNT7J4KM79MSA` (writing-quality) - disposition: none > The README's outcome table creates a second list of supported outcomes and their exit codes. A new or changed outcome can leave this user-facing table silently stale even while the wrapper returns the correct code. > > The source is `OUTCOMES` and `OUTCOME_EXIT` in `src/contract/types.ts`. Source members: `fork-skip`, `service-unreachable`, `auth-failed`, `forge-host-mismatch`, `ask-conflict`, `stuck`, `partial-coverage`, `grounding-pending`, `clean-zero-findings`, `clean-findings`, `head-moved`. Copied outcome members: `fork-skip`, `service-unreachable`, `auth-failed`, `forge-host-mismatch`, `ask-conflict`, `stuck`, `partial-coverage`, `grounding-pending`, `clean-zero-findings`, `clean-findings`, `head-moved`. No member appears in only one list, and the exits agree today. The table also includes `superseded`, explicitly marked as a flag rather than an outcome; `WaitResult.superseded` defines that flag. > > Point readers to those definitions for the membership and exit mapping. Keep the non-obvious recovery and timing guidance as focused prose near the existing error and rerun sections, without a complete outcome roster or row count. This preserves what users need to do while removing the duplicate set. > > I compared the table with `OUTCOMES`, `OUTCOME_EXIT`, `WaitResult`, and `runWrapper()` in `src/wrapper/orchestrator.ts`; this is a static comparison, not a runtime probe. Generation of this README table from the code would refute the maintenance risk, but the reviewed tree shows a handwritten Markdown table. ### medium — The composition paragraph copies the reconciler count and members - claim: `01M44FXXFPF39NW64Q3QVSFQM0` - anchor: `README.md` (snippet) ``` `src/main.ts` is the only place the modules meet. It reads the forge credentials and `GITHUB_REPOSITORY`, builds the forge client, derives the run's inputs from the event payload, and hands the orchestrator its collaborators: the wait protocol, the clock, and the three reconcilers (summary, inline, dispositions). The orchestrator's exit code becomes the process exit code, so the job's conclusion is the outcome table above. ``` - lens: writing-quality · arm: default - verdicts: 2 valid / 0 invalid / 0 uncertain - pass `01M44G2XZ0AJBP8KMP9MDVW8X9` · valid: The exact README paragraph counts and names summary, inline, and dispositions as the three reconcilers. The reviewer reports the same members in WrapperDeps in src/contract/types.ts and the wiring in src/main.ts. That is a complete copy of the code-defined collaborator set with no current mismatch. Refer to WrapperDeps while keeping the useful wiring and process-exit explanation. Historical matching verdicts supply no refutation. - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README paragraph counts and names summary, inline, and dispositions as the three reconcilers. The reviewer reports that WrapperDeps in src/contract/types.ts defines precisely those collaborators and src/main.ts supplies them. The count and member list copy a code-defined set that can drift. Refer to WrapperDeps while retaining the wiring and process-exit explanation. No current member mismatch is reported. - duplicates: `01M44HTPWS8WS1WGTC6R50VBKQ` (writing-quality) - disposition: none > The composition paragraph gives readers a second definition of how many reconcilers the wrapper wires. Adding or removing one in `WrapperDeps` or `src/main.ts` can leave the count and parenthesized names wrong without affecting the build. > > `WrapperDeps` defines the injected reconciler collaborators as `summary`, `inline`, and `dispositions`; `src/main.ts` supplies the same members. The README copy says `three reconcilers (summary, inline, dispositions)`. Both sets have those three members and none appears in only one set today. > > Replace the count and list with a pointer to `WrapperDeps`, such as saying that `src/main.ts` supplies the orchestrator's collaborators defined there. The paragraph can keep the distinct explanation that the orchestrator's exit code becomes the process exit code. > > I compared this paragraph with `WrapperDeps` in `src/contract/types.ts` and the dependency object passed to `runWrapper()` in `src/main.ts`; this was a static source comparison. The count would cease to be a duplicate only if this prose were the source consumed to build that dependency contract, which the reviewed code does not show. ### medium — The recovery table restates the remedies already emitted in job logs - claim: `01M44HT0GJRH67A2T0S35D09N0` - anchor: `README.md` (snippet) ``` | Message | Recovery | | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `provider session limit reached` | Wait for the session limit to reset or ask the operator to lift it. | | `selected model at capacity` | Wait for the model to accept new work. | | `agent execution failed` | Ask the operator to inspect the failed agent logs and correct the reported error. | | `sandbox infrastructure failed` | Ask the operator to check sandbox provisioning, controller connectivity, and resources. | | `execution failed for an unrecognized reason` | Copy the quoted code from `(code "…")` on that log line and ask the operator to look it up in the review service logs. | ``` - lens: writing-quality · arm: default - verdicts: 1 valid / 0 invalid / 0 uncertain - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README table gives a complete inventory of recovery messages and actions. The reviewer identifies four selected codes plus an unknown fallback in src/wrapper/remedies.ts, and reports that formatExecutionRecovery already emits the corresponding remedies in job logs; the bundled @j4k/review formatter supplies the labels. The copied set can stale even though no difference is reported now. Direct operators to the emitted recovery text and maintainers to the formatter. - disposition: none > The recovery table is a second inventory of execution-failure cases and remedies, so a new or changed failure mapping can leave the README giving stale instructions. src/wrapper/remedies.ts defines the selected codes provider-session-limit, provider-capacity, agent-execution-failed, and infrastructure-failure, plus an unknown-code fallback; the bundled @j4k/review formatter supplies their displayed message labels. The table covers those four cases and the fallback, with no member present on only one side at this revision. Replace the table with a direction to follow the recovery text on the job log line and a pointer to formatExecutionRecovery for maintainers. That keeps the operator action and quoted-code fallback available where they are actually produced without copying the set. I compared the table with executionRemedies and formatExecutionRecovery in src/wrapper/remedies.ts and with the bundled formatExecutionFailure mapping in dist/index.mjs; this is a source comparison, not an execution test. A newly mapped code or remedy change without a table edit would establish the predicted drift; the copy currently agrees. ### medium — The environment contract copies the required-variable inventory - claim: `01M44HVQDW8RY9BSA3AD87VP0C` - anchor: `README.md` (snippet) ``` ## Environment contract | Variable | Meaning | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `REVIEW_SERVICE_URL` | Base URL of the review service (an org-managed variable). Must parse as an `https:` URL and must not end with `/v1`, which the client appends. | | `REVIEW_CAPABILITY_TOKEN` | Capability token for the service's `/v1` API (an org secret). | | `FORGEJO_TOKEN` | The job's own task token, used for every forge write. | | `GITHUB_API_URL` | Forge API base, supplied by the runner. | | `GITHUB_EVENT_*`, `GITHUB_RUN_ID`, … | Standard runner event environment; the wrapper's PR inputs come from the event payload. | | `GITHUB_RUN_ATTEMPT` | Forgejo run attempt (1-based). Required on every path. The wrapper re-triages stalled or failed triage only when this is ≥ 2. | The forge and runner variables — `FORGEJO_TOKEN`, `GITHUB_API_URL`, `GITHUB_EVENT_*`, `GITHUB_RUN_ID`, `GITHUB_RUN_ATTEMPT`, `GITHUB_REPOSITORY`, `GITHUB_SERVER_URL` — are required on every path. `REVIEW_SERVICE_URL` and `REVIEW_CAPABILITY_TOKEN` are required only on the same-repository path: a fork pull request returns at the fork gate without opening a service session, so neither is ever read. Where a variable is required, a missing one is a configuration defect and fails the run immediately rather than sending an empty-string request. ``` - lens: writing-quality · arm: default - verdicts: 1 valid / 0 invalid / 0 uncertain - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README passage presents a required-variable inventory. The reviewer lists the requireEnv inputs in readWrapperInputs, forgeCredentials, and reviewCredentials and reports that the README covers those names, using GITHUB_EVENT_* shorthand, with no established missing member. It is a separately maintained copy that can silently stale. Point to the source functions for membership, while keeping the fork-path requirement and immediate-failure rule as non-inventory guidance. Medium fits absent a proved current mismatch. - disposition: none > The table and following sentence maintain a second list of variables the wrapper requires, so a new required read can leave setup documentation silently incomplete. In the source, readWrapperInputs requires GITHUB_EVENT_NAME, GITHUB_EVENT_PATH, GITHUB_REPOSITORY, GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT, and GITHUB_SERVER_URL; forgeCredentials requires GITHUB_API_URL and FORGEJO_TOKEN; reviewCredentials requires REVIEW_SERVICE_URL and REVIEW_CAPABILITY_TOKEN after the fork gate. The README covers those ten through explicit names and the GITHUB_EVENT_* shorthand; no known member appears only on one side, though the shorthand does not tell a reader which event fields are read. Point to these functions for the required names, while keeping the conditional fork-gate rule and immediate missing-value failure as prose; keep org-secret and runner provenance beside the corresponding definitions if needed. This preserves the configuration decisions without another variable set. I compared this section with src/main.ts, src/wrapper/event-context.ts, and src/credentials.ts; no runtime check was needed for the inventory. A new requireEnv call with no README update would establish the stale-copy failure, while an environment name available to readers only here would refute the proposed deletion; none appears in this tree. ### medium — The WrapperInputs header falsely says its fields are never computed - claim: `01M44HRFXHK0QP5RCK8NKVCP1J` - anchor: `src/contract/types.ts` (snippet) ``` // ---- Inputs (contract §2: derived from the event, never computed) ---- ``` - lens: writing-quality · arm: default - verdicts: 1 valid / 0 invalid / 0 uncertain - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact header claims the inputs are never computed. The reviewer reports that readWrapperInputs computes isFork, normalizes the forge host, parses the attempt, and constructs reTriageAskKey. These concrete operations contradict the blanket wording and could mislead a maintainer about the contract. Remove the false phrase while retaining a useful contract pointer; the unavailable external section does not undo the observed conflict in the subject code. - disposition: none > The header can lead a maintainer to treat local derivation as a contract violation, even though the interface requires derived values. readWrapperInputs calculates isFork by comparing repository names, lowercases the parsed forge host, parses the run attempt, and constructs reTriageAskKey; those values are not simply fields read from the event. Replace the header with a current description such as “Wrapper inputs from the runner and forge, including locally derived values,” preserving the contract pointer without the false restriction. I read this interface and both event branches in src/wrapper/event-context.ts; this is a source trace, not a runtime test. The external contract cited as §2 is unavailable in this snapshot, so its intended wording cannot be verified here; the subject code itself establishes that the blanket “never computed” statement is false. ### medium — Input derivation comment duplicates the two PR metadata sources - claim: `01M44FWJB8RYQ4ZV29426J4CPN` - anchor: `src/wrapper/event-context.ts` (snippet) ``` // Input derivation (roadmap 1.2, contract §2). PR metadata is read from the // event payload or fetched from the forge. Merge base, digest and scope ``` - lens: restated-sets · arm: default - verdicts: 2 valid / 0 invalid / 0 uncertain - pass `01M44G2XZ0AJBP8KMP9MDVW8X9` · valid: The exact comment presents event payload and forge as the complete PR metadata source set. The reviewer identifies the two readWrapperInputs branches in src/wrapper/event-context.ts, with readEventPayload and forge.getPull defining those same members. This adjacent comment copies the code-defined set and can drift; delete its source-list sentence. No current member mismatch is reported, so medium severity fits. - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact adjacent comment says PR metadata comes from the event payload or forge. The reviewer identifies readEventPayload and forge.getPull branches in src/wrapper/event-context.ts as the code-defined two-source set, with no current difference. This comment restates that set and can drift; delete the source-list sentence while keeping distinct intent. Medium severity fits. - disposition: none > The opening comment gives a complete account of where PR metadata comes from. A later change to either input path would need to update this second account, or maintainers could rely on a stale description while reviewing the fork gate. > > In `readWrapperInputs`, the `pull_request_target` branch gets head, base, and pull-number fields from `readEventPayload(eventPath)`, while the `workflow_dispatch` branch gets them from `forge.getPull(prNumber)`. The source members are event payload and forge; the comment copies event payload and forge. No member occurs in only one list, so the copy currently agrees. > > Delete the source-list sentence from this comment. The two branches immediately below it define the sources, and the rest of the comment can retain its distinct explanation of work delegated to the service and the fork gate. > > I read the full `src/wrapper/event-context.ts` and traced both branches of `readWrapperInputs`, then checked `src/wrapper/event-context.test.ts` for their exercised paths. This is static reasoning, not a runtime reproduction. The comment is neither an input the system reads nor a dated record or example; readers can inspect the adjacent source. The branch bodies are the decisive evidence for the two current members. A rule fixing those sources independently of this code would refute the maintenance-risk premise, but none appears in the subject. ### low — README security paragraph duplicates internal credential call order and test coverage - claim: `01M44FWA4NTNW284GH2SQT810M` - anchor: `README.md` (snippet) ``` - Forgejo does pass secrets to fork `pull_request_target` runs, so the real boundary is code-path ordering: `readWrapperInputs` decides whether the pull request is a fork **before** `reviewCredentials()` is called. `forgeCredentials()`, which reads the forge task token, runs before the gate on every path. A fork run returns at the gate, so nothing `reviewCredentials()` reads is read on that path. The isolation test in `src/credentials.test.ts` checks which modules import the credentials module, and an orchestrator unit test checks that a fork run never opens a review session. ``` - lens: project-docs · arm: default - verdicts: 2 valid / 0 invalid / 0 uncertain - pass `01M44G2XZ0AJBP8KMP9MDVW8X9` · valid: The exact README excerpt documents internal credential call order and two test checks. The reviewer reports that this passage was changed in the diff and traces forgeCredentials, readWrapperInputs, the fork return, and the deferred openSession call. Those details are implementation and test inventory in a human onboarding README; the project-docs rule puts how it works in code. Keep the user-facing fork guarantee as a short security summary. Low severity fits a wrong-home finding. - pass `01M44HX1VSNH8MET4Q0243HBNG` · valid: The exact README excerpt narrates credential helper order and names two tests. The reviewer reports this passage was edited and traces forgeCredentials before readWrapperInputs, then the fork return before the deferred reviewCredentials call. That is internal implementation and test inventory in a human-onboarding README; the project-docs skill assigns how-it-works detail to code and tests. Keep the user-facing fork credential guarantee as a short summary. Low severity fits the wrong-home finding. - duplicates: `01M44HVDF2E0TB2KVFQ1YWN547` (project-docs) - disposition: none > The edited security paragraph makes the README a second source for internal call order and test inventory. That creates a concrete maintenance cost for action users: this edit already corrects the earlier claim that the fork gate ran before any credential module was called, although the unchanged entry point calls `forgeCredentials()` first. A later refactor can again leave readers with a false account of which token is read on fork runs. > > The changed passage says `readWrapperInputs` decides the fork before `reviewCredentials()` runs, that `forgeCredentials()` runs first, and that two named tests check module imports and session opening. In `src/main.ts`, the forge initializer calls `forgeCredentials()`, then awaits `readWrapperInputs(forge)`, and supplies `reviewCredentials()` only inside the `openSession` thunk. In `src/wrapper/orchestrator.ts`, `runWrapper` returns from `reconcileForkSkip` before calling `review`, where `deps.openSession()` occurs. The previous README wording said the fork gate was evaluated before the credentials module was ever called; the diff changes that wording without changing this call path. > > Keep the user-facing guarantee in README in one sentence: fork runs do not open a review session or read the review capability token. Remove the function-by-function sequence and test inventory from this passage; their homes are the existing source and tests. The README exception in the project-docs skill covers human onboarding and a short security summary, while this call sequence and test inventory restate implementation rather than explain a user-facing choice or a trade-off. > > I read the complete README, the changed `src/contract/types.ts` and `src/wrapper/event-context.ts`, `src/main.ts`, `src/credentials.ts`, `src/wrapper/orchestrator.ts`, and the credential and fork-gate tests. This is a static source trace; I did not run the action. No `CONTEXT.md`, `CONTEXT-MAP.md`, or ADR exists in the tree. An explicit repository documentation convention requiring the README to carry this internal security call trace would refute the placement finding; I found none in README or AGENTS.md. ## Other claims - grounding-pending (0) - ungrounded (0) - rejected (0) - duplicate-of (4) - `01M44HSEXSK05FNT7J4KM79MSA` medium — The outcomes table duplicates the code-defined outcome set → `01M44FXC8EWSST2AF4EADZXNJX` - `01M44HTPWS8WS1WGTC6R50VBKQ` medium — The composition paragraph copies the reconciler set from main.ts → `01M44FXXFPF39NW64Q3QVSFQM0` - `01M44HVDF2E0TB2KVFQ1YWN547` low — README security model duplicates the internal credential call sequence → `01M44FWA4NTNW284GH2SQT810M` - `01M44HVZRQB3CNWKBEZS6Y25JB` medium — The Action inputs table repeats action.yml → `01M44FWM69QYH21FMRAYX73KZD` - unadjudicated (0) ## Coverage Coverage pass: 01M44HJR3QV5JN9PKQ4J2X163F Accounting: complete Slot health: healthy | lens | part | arm | unit status | runs | loss | | --- | --- | --- | --- | --- | --- | | general-bug | whole | default | no-claims | 1 | no | | writing-quality | whole | default | claims-emitted | 1 | no | | test-trimming | whole | default | no-claims | 1 | no | | restated-sets | whole | default | no-claims | 1 | no | | project-docs | whole | default | claims-emitted | 1 | no |
README.md Outdated
Lines 49-50
@ -50,3 +49,2 @@
credentials module is ever called. On the fork path the capability token environment
variable is never read — enforced by a static isolation test and an orchestrator unit
test.
code-path ordering: the fork gate is evaluated from the event payload **before**
`reviewCredentials()` is called. The forge task token (`FORGEJO_TOKEN`) is read before

medium — Security paragraph attributes every fork decision to the event payload

A reader assessing the credential boundary could infer that a dispatch run decides whether a pull request is a fork without consulting the forge. That is false for workflow_dispatch: its event payload contains a PR number, and the wrapper fetches the pull request before it can set isFork.

The paragraph says the fork gate is evaluated “from the event payload” before reviewCredentials() runs. Keep the ordering claim, but say that readWrapperInputs derives the verdict from the available PR data before the orchestrator opens a review session. This preserves the security guarantee without implying that every verdict comes from the payload.

I traced readWrapperInputs in src/wrapper/event-context.ts: the dispatch branch calls forge.getPull(prNumber) and compares pull.headRepoFullName with slug, while the pull-request branch compares the payload head repository with slug. src/main.ts calls forgeCredentials() before readWrapperInputs, then supplies reviewCredentials() as an openSession thunk. runWrapper in src/wrapper/orchestrator.ts checks isFork before invoking that thunk. The dispatch test “flags a fork when the fetched head repo differs from the slug” confirms the intended source. This is a source trace, not a runtime reproduction.

The claim would be refuted if the dispatch verdict actually came from the event payload; the dispatch branch and its test show that it comes from the fetched pull request.

lens writing-quality · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44F37NYJZ9G995T77RXWWW2 of review 01M44EVAMWZ9QXBSWC339418SG

<!-- review:claim:01M44F37NYJZ9G995T77RXWWW2 --> **medium** — Security paragraph attributes every fork decision to the event payload > A reader assessing the credential boundary could infer that a dispatch run decides whether a pull request is a fork without consulting the forge. That is false for workflow_dispatch: its event payload contains a PR number, and the wrapper fetches the pull request before it can set isFork. > > The paragraph says the fork gate is evaluated “from the event payload” before reviewCredentials() runs. Keep the ordering claim, but say that readWrapperInputs derives the verdict from the available PR data before the orchestrator opens a review session. This preserves the security guarantee without implying that every verdict comes from the payload. > > I traced readWrapperInputs in src/wrapper/event-context.ts: the dispatch branch calls forge.getPull(prNumber) and compares pull.headRepoFullName with slug, while the pull-request branch compares the payload head repository with slug. src/main.ts calls forgeCredentials() before readWrapperInputs, then supplies reviewCredentials() as an openSession thunk. runWrapper in src/wrapper/orchestrator.ts checks isFork before invoking that thunk. The dispatch test “flags a fork when the fetched head repo differs from the slug” confirms the intended source. This is a source trace, not a runtime reproduction. > > The claim would be refuted if the dispatch verdict actually came from the event payload; the dispatch branch and its test show that it comes from the fetched pull request. lens `writing-quality` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44F37NYJZ9G995T77RXWWW2` of review `01M44EVAMWZ9QXBSWC339418SG`
Author
Owner

Fixed in 67a5202. The bullet now says readWrapperInputs decides whether the pull request is a fork before reviewCredentials() is called, without claiming the verdict always comes from the payload. On workflow_dispatch it comes from the fetched pull request.

<!-- gh-feedback:reply-to:123339 --> Fixed in 67a5202. The bullet now says `readWrapperInputs` decides whether the pull request is a fork before `reviewCredentials()` is called, without claiming the verdict always comes from the payload. On `workflow_dispatch` it comes from the fetched pull request.
jercik marked this conversation as resolved
README.md Outdated
Lines 51-52
@ -52,1 +49,4 @@
test.
code-path ordering: the fork gate is evaluated from the event payload **before**
`reviewCredentials()` is called. The forge task token (`FORGEJO_TOKEN`) is read before
the gate, on every path. On the fork path `REVIEW_SERVICE_URL` and
`REVIEW_CAPABILITY_TOKEN` are never read: a static isolation test restricts the

medium — Fork security paragraph copies the review credential variable set

The fork guarantee is tied to a hard-coded list of review service variables in the README. If reviewCredentials() gains another required service variable, the fork gate can still skip the whole function while this security description silently becomes incomplete. src/credentials.ts defines the required review service variables through its requireEnv calls: REVIEW_SERVICE_URL and REVIEW_CAPABILITY_TOKEN; the optional INPUT_EXPECTED-SERVICE-ORIGIN action input is a separate setting. The changed README sentence covers those same two required service variables, with no member appearing in only one set. Say instead that the fork path returns before reviewCredentials() runs, so its service configuration variables are not read; the function remains the source for their names. I traced src/main.ts from forgeCredentials() through readWrapperInputs() to runWrapper(), whose fork branch returns before calling openSession(), and read the requireEnv calls in src/credentials.ts. This is static reasoning; I did not run the action. The claim would be refuted by an independent, authoritative contract that deliberately fixes these two names apart from the function, but none of the subject files I inspected defines such a set.

lens restated-sets · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44F3SBF4RFT93KDQPMT68T4 of review 01M44EVAMWZ9QXBSWC339418SG

<!-- review:claim:01M44F3SBF4RFT93KDQPMT68T4 --> **medium** — Fork security paragraph copies the review credential variable set > The fork guarantee is tied to a hard-coded list of review service variables in the README. If `reviewCredentials()` gains another required service variable, the fork gate can still skip the whole function while this security description silently becomes incomplete. `src/credentials.ts` defines the required review service variables through its `requireEnv` calls: `REVIEW_SERVICE_URL` and `REVIEW_CAPABILITY_TOKEN`; the optional `INPUT_EXPECTED-SERVICE-ORIGIN` action input is a separate setting. The changed README sentence covers those same two required service variables, with no member appearing in only one set. Say instead that the fork path returns before `reviewCredentials()` runs, so its service configuration variables are not read; the function remains the source for their names. I traced `src/main.ts` from `forgeCredentials()` through `readWrapperInputs()` to `runWrapper()`, whose fork branch returns before calling `openSession()`, and read the `requireEnv` calls in `src/credentials.ts`. This is static reasoning; I did not run the action. The claim would be refuted by an independent, authoritative contract that deliberately fixes these two names apart from the function, but none of the subject files I inspected defines such a set. lens `restated-sets` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44F3SBF4RFT93KDQPMT68T4` of review `01M44EVAMWZ9QXBSWC339418SG`
Author
Owner

Fixed in 67a5202. The bullet now says a fork run returns at the gate, so nothing reviewCredentials() reads is read on that path. It no longer lists the variables.

<!-- gh-feedback:reply-to:123340 --> Fixed in 67a5202. The bullet now says a fork run returns at the gate, so nothing `reviewCredentials()` reads is read on that path. It no longer lists the variables.
jercik marked this conversation as resolved
README.md Outdated
@ -53,0 +50,4 @@
`reviewCredentials()` is called. The forge task token (`FORGEJO_TOKEN`) is read before
the gate, on every path. On the fork path `REVIEW_SERVICE_URL` and
`REVIEW_CAPABILITY_TOKEN` are never read: a static isolation test restricts the
credentials module's importers to `src/main.ts`, and an orchestrator unit test checks

medium — Security model copies the credentials module importer set

The security description fixes the module's production importers in prose. If the import graph and its isolation test change, this sentence needs a separate edit and can silently send a reviewer to the wrong entry point. It says the credentials module's importers are restricted to src/main.ts. In the subject, src/main.ts is the sole production file importing src/credentials.ts; the src/credentials.test.ts isolation check scans production files and excludes test files. The source set and this copy each contain src/main.ts, with no member in only one. Replace the file name here with a pointer to the isolation test in src/credentials.test.ts, which checks the current import graph. I traced the import in src/main.ts and the sourceFiles/importSpecifiers check in src/credentials.test.ts; this is a static comparison, not a test run. The claim would be refuted if this sentence were itself the authoritative rule for importer membership, but the test derives importers from the source files.

lens restated-sets · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44F2NHWHVC7556RHN72P4AY of review 01M44EVAMWZ9QXBSWC339418SG

<!-- review:claim:01M44F2NHWHVC7556RHN72P4AY --> **medium** — Security model copies the credentials module importer set > The security description fixes the module's production importers in prose. If the import graph and its isolation test change, this sentence needs a separate edit and can silently send a reviewer to the wrong entry point. It says the credentials module's importers are restricted to `src/main.ts`. In the subject, `src/main.ts` is the sole production file importing `src/credentials.ts`; the `src/credentials.test.ts` isolation check scans production files and excludes test files. The source set and this copy each contain `src/main.ts`, with no member in only one. Replace the file name here with a pointer to the isolation test in `src/credentials.test.ts`, which checks the current import graph. I traced the import in `src/main.ts` and the `sourceFiles`/`importSpecifiers` check in `src/credentials.test.ts`; this is a static comparison, not a test run. The claim would be refuted if this sentence were itself the authoritative rule for importer membership, but the test derives importers from the source files. lens `restated-sets` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44F2NHWHVC7556RHN72P4AY` of review `01M44EVAMWZ9QXBSWC339418SG`
Author
Owner

Fixed in 67a5202. The bullet now points at the isolation test in src/credentials.test.ts for which modules may import the credentials module, instead of naming the importer.

<!-- gh-feedback:reply-to:123338 --> Fixed in 67a5202. The bullet now points at the isolation test in `src/credentials.test.ts` for which modules may import the credentials module, instead of naming the importer.
jercik marked this conversation as resolved
Lines 1-5
@ -2,7 +2,7 @@
// the event payload or fetched from the forge — nothing here is computed: no
// merge base, no digest, no scope canonicalization, and the PR title and body
// are never read. The fork verdict is derived here so the orchestrator can
// evaluate it before any credential module is ever called.
// evaluate it before reviewCredentials() is ever called.

low — Input comment says nothing is computed despite deriving wrapper fields

A maintainer reading this module’s opening contract could think it only copies external values, then miss the local derivation of the fork verdict and run keys when changing input handling. The absolute claim “nothing here is computed” contradicts this file.

readWrapperInputs calculates isFork by comparison, parses and validates runAttempt, lowercases the forge host, and constructs reTriageAskKey. The comment itself later says the fork verdict is derived here. Under the writing standard’s precise-language and no-op guidance, delete the absolute sentence or narrow it to the review data that is deliberately left to the service. Preserve the useful boundary that PR metadata comes from the payload or forge, that title and body are not read, and that the fork verdict is available before reviewCredentials() runs.

I read the full src/wrapper/event-context.ts file and its WrapperInputs type in src/contract/types.ts; the assignments in readWrapperInputs establish the contradiction. This is a static source trace. The claim would be refuted if those assignments merely copied already-derived fields, but the expressions visibly compute new values.

lens writing-quality · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44F97BWXVM7TQVQCNGYMPX6 of review 01M44EVAMWZ9QXBSWC339418SG

<!-- review:claim:01M44F97BWXVM7TQVQCNGYMPX6 --> **low** — Input comment says nothing is computed despite deriving wrapper fields > A maintainer reading this module’s opening contract could think it only copies external values, then miss the local derivation of the fork verdict and run keys when changing input handling. The absolute claim “nothing here is computed” contradicts this file. > > readWrapperInputs calculates isFork by comparison, parses and validates runAttempt, lowercases the forge host, and constructs reTriageAskKey. The comment itself later says the fork verdict is derived here. Under the writing standard’s precise-language and no-op guidance, delete the absolute sentence or narrow it to the review data that is deliberately left to the service. Preserve the useful boundary that PR metadata comes from the payload or forge, that title and body are not read, and that the fork verdict is available before reviewCredentials() runs. > > I read the full src/wrapper/event-context.ts file and its WrapperInputs type in src/contract/types.ts; the assignments in readWrapperInputs establish the contradiction. This is a static source trace. The claim would be refuted if those assignments merely copied already-derived fields, but the expressions visibly compute new values. lens `writing-quality` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44F97BWXVM7TQVQCNGYMPX6` of review `01M44EVAMWZ9QXBSWC339418SG`
Author
Owner

Fixed in 67a5202. The header now says PR metadata is read from the payload or fetched from the forge, and that merge base, digest and scope canonicalization are left to the service. The absolute "nothing here is computed" is gone.

<!-- gh-feedback:reply-to:123341 --> Fixed in 67a5202. The header now says PR metadata is read from the payload or fetched from the forge, and that merge base, digest and scope canonicalization are left to the service. The absolute "nothing here is computed" is gone.
jercik marked this conversation as resolved
docs: point the fork-gate bullet at its sources instead of listing them
Some checks failed
commit-msg / commitlint (pull_request) Successful in 17s
Checks / quality-checks (pull_request) Successful in 49s
Review / Review (pull_request_target) Failing after 10m25s
67a520252f
The bullet now says readWrapperInputs makes the fork decision (from the payload
or, on workflow_dispatch, the fetched pull request), names no review variables or
importers, and points at the isolation test. The event-context header no longer
claims nothing is computed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Replying to comment #123337

This is review 01M44EVAMWZ9QXBSWC339418SG of a4e7bd0. Its four inline findings are fixed in 67a5202 and answered in their threads. The four summary-only findings are about README text this PR doesn't change, and each is already handled by the PR that owns that text:

  • 01M44F667G03T6M1T1XWP37C32 ("Environment contract overstates the required event variables") is valid. #32 replaces that list, wildcard included, with a pointer to forgeCredentials, readWrapperInputs and src/main.ts.
  • 01M44F4QH9RYXHZQHM1XGBBJVK ("Outcome table restates the code-defined outcome set and exit mapping") is valid. #30 replaces the table with a pointer to OUTCOMES and OUTCOME_EXIT.
  • 01M44F72T06SY944TY7EP2AVCV ("Composition paragraph restates the reconciler set") is valid. #32 points at WrapperDeps instead.
  • 01M44F84QVWWW9MGXBWTR4ASHS ("Recovery table restates the selected execution failure remedies") is valid. #34 replaces the table with the remedy printed on the failure log line.
> Replying to comment #123337 This is review `01M44EVAMWZ9QXBSWC339418SG` of `a4e7bd0`. Its four inline findings are fixed in 67a5202 and answered in their threads. The four summary-only findings are about README text this PR doesn't change, and each is already handled by the PR that owns that text: - `01M44F667G03T6M1T1XWP37C32` ("Environment contract overstates the required event variables") is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32 replaces that list, wildcard included, with a pointer to `forgeCredentials`, `readWrapperInputs` and `src/main.ts`. - `01M44F4QH9RYXHZQHM1XGBBJVK` ("Outcome table restates the code-defined outcome set and exit mapping") is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/30 replaces the table with a pointer to `OUTCOMES` and `OUTCOME_EXIT`. - `01M44F72T06SY944TY7EP2AVCV` ("Composition paragraph restates the reconciler set") is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32 points at `WrapperDeps` instead. - `01M44F84QVWWW9MGXBWTR4ASHS` ("Recovery table restates the selected execution failure remedies") is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/34 replaces the table with the remedy printed on the failure log line.
Lines 40-46
@ -49,4 +49,6 @@
code-path ordering: the fork gate is evaluated from the event payload **before** the
credentials module is ever called. On the fork path the capability token environment
variable is never read — enforced by a static isolation test and an orchestrator unit
test.
code-path ordering: `readWrapperInputs` decides whether the pull request is a fork
**before** `reviewCredentials()` is called. `forgeCredentials()`, which reads the forge
task token, runs before the gate on every path. A fork run returns at the gate, so
nothing `reviewCredentials()` reads is read on that path. The isolation test in
`src/credentials.test.ts` checks which modules import the credentials module, and an
orchestrator unit test checks that a fork run never opens a review session.

low — README security paragraph duplicates internal credential call order and test coverage

The edited security paragraph makes the README a second source for internal call order and test inventory. That creates a concrete maintenance cost for action users: this edit already corrects the earlier claim that the fork gate ran before any credential module was called, although the unchanged entry point calls forgeCredentials() first. A later refactor can again leave readers with a false account of which token is read on fork runs.

The changed passage says readWrapperInputs decides the fork before reviewCredentials() runs, that forgeCredentials() runs first, and that two named tests check module imports and session opening. In src/main.ts, the forge initializer calls forgeCredentials(), then awaits readWrapperInputs(forge), and supplies reviewCredentials() only inside the openSession thunk. In src/wrapper/orchestrator.ts, runWrapper returns from reconcileForkSkip before calling review, where deps.openSession() occurs. The previous README wording said the fork gate was evaluated before the credentials module was ever called; the diff changes that wording without changing this call path.

Keep the user-facing guarantee in README in one sentence: fork runs do not open a review session or read the review capability token. Remove the function-by-function sequence and test inventory from this passage; their homes are the existing source and tests. The README exception in the project-docs skill covers human onboarding and a short security summary, while this call sequence and test inventory restate implementation rather than explain a user-facing choice or a trade-off.

I read the complete README, the changed src/contract/types.ts and src/wrapper/event-context.ts, src/main.ts, src/credentials.ts, src/wrapper/orchestrator.ts, and the credential and fork-gate tests. This is a static source trace; I did not run the action. No CONTEXT.md, CONTEXT-MAP.md, or ADR exists in the tree. An explicit repository documentation convention requiring the README to carry this internal security call trace would refute the placement finding; I found none in README or AGENTS.md.

lens project-docs · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44FWA4NTNW284GH2SQT810M of review 01M44FMS560BRRJR823QSZRW2E

<!-- review:claim:01M44FWA4NTNW284GH2SQT810M --> **low** — README security paragraph duplicates internal credential call order and test coverage > The edited security paragraph makes the README a second source for internal call order and test inventory. That creates a concrete maintenance cost for action users: this edit already corrects the earlier claim that the fork gate ran before any credential module was called, although the unchanged entry point calls `forgeCredentials()` first. A later refactor can again leave readers with a false account of which token is read on fork runs. > > The changed passage says `readWrapperInputs` decides the fork before `reviewCredentials()` runs, that `forgeCredentials()` runs first, and that two named tests check module imports and session opening. In `src/main.ts`, the forge initializer calls `forgeCredentials()`, then awaits `readWrapperInputs(forge)`, and supplies `reviewCredentials()` only inside the `openSession` thunk. In `src/wrapper/orchestrator.ts`, `runWrapper` returns from `reconcileForkSkip` before calling `review`, where `deps.openSession()` occurs. The previous README wording said the fork gate was evaluated before the credentials module was ever called; the diff changes that wording without changing this call path. > > Keep the user-facing guarantee in README in one sentence: fork runs do not open a review session or read the review capability token. Remove the function-by-function sequence and test inventory from this passage; their homes are the existing source and tests. The README exception in the project-docs skill covers human onboarding and a short security summary, while this call sequence and test inventory restate implementation rather than explain a user-facing choice or a trade-off. > > I read the complete README, the changed `src/contract/types.ts` and `src/wrapper/event-context.ts`, `src/main.ts`, `src/credentials.ts`, `src/wrapper/orchestrator.ts`, and the credential and fork-gate tests. This is a static source trace; I did not run the action. No `CONTEXT.md`, `CONTEXT-MAP.md`, or ADR exists in the tree. An explicit repository documentation convention requiring the README to carry this internal security call trace would refute the placement finding; I found none in README or AGENTS.md. lens `project-docs` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44FWA4NTNW284GH2SQT810M` of review `01M44FMS560BRRJR823QSZRW2E`
jercik marked this conversation as resolved
Lines 1-2
@ -3,3 +1,2 @@
// merge base, no digest, no scope canonicalization, and the PR title and body
// are never read. The fork verdict is derived here so the orchestrator can
// evaluate it before any credential module is ever called.
// Input derivation (roadmap 1.2, contract §2). PR metadata is read from the
// event payload or fetched from the forge. Merge base, digest and scope

medium — Input derivation comment duplicates the two PR metadata sources

The opening comment gives a complete account of where PR metadata comes from. A later change to either input path would need to update this second account, or maintainers could rely on a stale description while reviewing the fork gate.

In readWrapperInputs, the pull_request_target branch gets head, base, and pull-number fields from readEventPayload(eventPath), while the workflow_dispatch branch gets them from forge.getPull(prNumber). The source members are event payload and forge; the comment copies event payload and forge. No member occurs in only one list, so the copy currently agrees.

Delete the source-list sentence from this comment. The two branches immediately below it define the sources, and the rest of the comment can retain its distinct explanation of work delegated to the service and the fork gate.

I read the full src/wrapper/event-context.ts and traced both branches of readWrapperInputs, then checked src/wrapper/event-context.test.ts for their exercised paths. This is static reasoning, not a runtime reproduction. The comment is neither an input the system reads nor a dated record or example; readers can inspect the adjacent source. The branch bodies are the decisive evidence for the two current members. A rule fixing those sources independently of this code would refute the maintenance-risk premise, but none appears in the subject.

lens restated-sets · arm default · tally 1 valid / 0 invalid / 0 uncertain
claim 01M44FWJB8RYQ4ZV29426J4CPN of review 01M44FMS560BRRJR823QSZRW2E

<!-- review:claim:01M44FWJB8RYQ4ZV29426J4CPN --> **medium** — Input derivation comment duplicates the two PR metadata sources > The opening comment gives a complete account of where PR metadata comes from. A later change to either input path would need to update this second account, or maintainers could rely on a stale description while reviewing the fork gate. > > In `readWrapperInputs`, the `pull_request_target` branch gets head, base, and pull-number fields from `readEventPayload(eventPath)`, while the `workflow_dispatch` branch gets them from `forge.getPull(prNumber)`. The source members are event payload and forge; the comment copies event payload and forge. No member occurs in only one list, so the copy currently agrees. > > Delete the source-list sentence from this comment. The two branches immediately below it define the sources, and the rest of the comment can retain its distinct explanation of work delegated to the service and the fork gate. > > I read the full `src/wrapper/event-context.ts` and traced both branches of `readWrapperInputs`, then checked `src/wrapper/event-context.test.ts` for their exercised paths. This is static reasoning, not a runtime reproduction. The comment is neither an input the system reads nor a dated record or example; readers can inspect the adjacent source. The branch bodies are the decisive evidence for the two current members. A rule fixing those sources independently of this code would refute the maintenance-risk premise, but none appears in the subject. lens `restated-sets` · arm `default` · tally 1 valid / 0 invalid / 0 uncertain claim `01M44FWJB8RYQ4ZV29426J4CPN` of review `01M44FMS560BRRJR823QSZRW2E`
jercik marked this conversation as resolved
Author
Owner

Replying to review comment #123531

Valid. The two branches of readWrapperInputs right below the comment already show where the PR metadata comes from, so the sentence is a second copy. This PR is in its second review round and the sentence doesn't make its fork-gate correction wrong, so #39, stacked on this PR, deletes it.

> Replying to review comment #123531 Valid. The two branches of `readWrapperInputs` right below the comment already show where the PR metadata comes from, so the sentence is a second copy. This PR is in its second review round and the sentence doesn't make its fork-gate correction wrong, so https://code.j4k.dev/j4k-oss/review-wrapper/pulls/39, stacked on this PR, deletes it.
Author
Owner

Replying to review comment #123532

Valid. The README needs the guarantee, not the call order or the names of the tests that check it, and the Composition section already shows where the gate sits in code. This PR is in its second review round and the extra detail is accurate, so #39, stacked on this PR, cuts the bullet to the guarantee: a fork run returns at the fork gate without opening a review session, so it never reads REVIEW_CAPABILITY_TOKEN.

> Replying to review comment #123532 Valid. The README needs the guarantee, not the call order or the names of the tests that check it, and the Composition section already shows where the gate sits in code. This PR is in its second review round and the extra detail is accurate, so https://code.j4k.dev/j4k-oss/review-wrapper/pulls/39, stacked on this PR, cuts the bullet to the guarantee: a fork run returns at the fork gate without opening a review session, so it never reads `REVIEW_CAPABILITY_TOKEN`.
Author
Owner

Replying to comment #123337

This is review 01M44FMS560BRRJR823QSZRW2E of 67a5202. Dispatch run 64134 completed it after run 63897 lost its writing-quality slot to model capacity. The two findings with threads are answered there. The other six are summary-only, about text this PR doesn't change:

  • 01M44FWM69QYH21FMRAYX73KZD (action-input table) is valid. #34 points the README at inputs in action.yml.
  • 01M44HT0GJRH67A2T0S35D09N0 (recovery table) is valid. #34 replaces the table with the remedy printed on the failure log line.
  • 01M44FXC8EWSST2AF4EADZXNJX (outcome table) is valid. #30 replaces the table with a pointer to OUTCOMES and OUTCOME_EXIT.
  • 01M44FXXFPF39NW64Q3QVSFQM0 (composition paragraph) is valid. #32 points at WrapperDeps.
  • 01M44HRFXHK0QP5RCK8NKVCP1J (the inputs header says "never computed") is valid: readWrapperInputs computes isFork and reTriageAskKey and lowercases the forge host. Tracked in #40, which drops the phrase. It targets main, because the header is older than this PR.
  • 01M44HVQDW8RY9BSA3AD87VP0C (the environment contract copies the required variables) is split. #32 replaces the variable list in the sentence after the table with pointers to the functions that read them. Replacing the table itself is held, as 01M44DH5HKQFA3T9JJSGAEFP8M was on #30. The table is the only place that tells a calling workflow which variables to set and what each must hold, and no single source file defines that set. Replacing it changes what the README promises those workflows, so it waits for that decision.

The four duplicate claims, 01M44HSEXSK05FNT7J4KM79MSA, 01M44HTPWS8WS1WGTC6R50VBKQ, 01M44HVDF2E0TB2KVFQ1YWN547 and 01M44HVZRQB3CNWKBEZS6Y25JB, are covered by the findings they duplicate.

> Replying to comment #123337 This is review `01M44FMS560BRRJR823QSZRW2E` of `67a5202`. Dispatch run 64134 completed it after run 63897 lost its writing-quality slot to model capacity. The two findings with threads are answered there. The other six are summary-only, about text this PR doesn't change: - `01M44FWM69QYH21FMRAYX73KZD` (action-input table) is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/34 points the README at `inputs` in `action.yml`. - `01M44HT0GJRH67A2T0S35D09N0` (recovery table) is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/34 replaces the table with the remedy printed on the failure log line. - `01M44FXC8EWSST2AF4EADZXNJX` (outcome table) is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/30 replaces the table with a pointer to `OUTCOMES` and `OUTCOME_EXIT`. - `01M44FXXFPF39NW64Q3QVSFQM0` (composition paragraph) is valid. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32 points at `WrapperDeps`. - `01M44HRFXHK0QP5RCK8NKVCP1J` (the inputs header says "never computed") is valid: `readWrapperInputs` computes `isFork` and `reTriageAskKey` and lowercases the forge host. Tracked in https://code.j4k.dev/j4k-oss/review-wrapper/pulls/40, which drops the phrase. It targets `main`, because the header is older than this PR. - `01M44HVQDW8RY9BSA3AD87VP0C` (the environment contract copies the required variables) is split. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32 replaces the variable list in the sentence after the table with pointers to the functions that read them. Replacing the table itself is held, as `01M44DH5HKQFA3T9JJSGAEFP8M` was on #30. The table is the only place that tells a calling workflow which variables to set and what each must hold, and no single source file defines that set. Replacing it changes what the README promises those workflows, so it waits for that decision. The four duplicate claims, `01M44HSEXSK05FNT7J4KM79MSA`, `01M44HTPWS8WS1WGTC6R50VBKQ`, `01M44HVDF2E0TB2KVFQ1YWN547` and `01M44HVZRQB3CNWKBEZS6Y25JB`, are covered by the findings they duplicate.
Author
Owner

Replying to comment #123337

The half of 01M44HVQDW8RY9BSA3AD87VP0C held above is declined: the environment table stays. It is the usage contract for someone wiring the action into another repository, and no single definition exists to point at, because src/credentials.ts, src/wrapper/event-context.ts and src/main.ts each read part of the set.

The other half stands. #32 replaces the variable list in the sentence after the table with pointers.

> Replying to comment #123337 The half of `01M44HVQDW8RY9BSA3AD87VP0C` held above is declined: the environment table stays. It is the usage contract for someone wiring the action into another repository, and no single definition exists to point at, because `src/credentials.ts`, `src/wrapper/event-context.ts` and `src/main.ts` each read part of the set. The other half stands. https://code.j4k.dev/j4k-oss/review-wrapper/pulls/32 replaces the variable list in the sentence after the table with pointers.
jercik merged commit 015660a2d8 into main 2026-10-05 09:31:24 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
j4k-oss/review-wrapper!33
No description provided.