Skip to content

feat: show what the change is on the job page - #783

Merged
HugoXK merged 1 commit into
mainfrom
dev1/373-change-story-slice1
Nov 15, 2026
Merged

HugoXK merged 1 commit into
mainfrom
dev1/373-change-story-slice1

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 15, 2026 •

Copy link
Copy Markdown
Owner

Written-by: HugoXK
Author: Fledge Platform Dev-1
Fixes #373

Pre-flight (the Lead now requires it on every dispatch): the card #373 is open, not closed; no earlier pull request exists for it — the only body mention of 373 anywhere is #101, closed 2026-11-09 and unrelated; three open pull requests touch files this one touches too — #784 and #782 (src/views/pages.ts, docs/architecture.md) and #775 (src/app.ts, docs/api.md, docs/architecture.md). They add different regions, so no textual conflict is expected, but if one lands first this branch is refreshed against main before the verdict.

What changed

Slice 1 of #373, and only slice 1. The job page proved the checks passed; it did not say what the change was, so a maintainer had to open the pull request to find out. The page now opens with a Why this change block above the evidence rows: the three sections the agent already writes into its PR body, plus the diff totals.

  • src/lib/changeStory.ts (new, pure) splits the PR body on its ## What changed / ## Risks / ## Ran headings — any heading level, and the bare Ran: line this repository's own PR template uses — and returns the three sections plus a duplicated flag. A heading the body does not carry returns null and the page renders not stated; it is never filled from another section. A repeated heading keeps the last occurrence and the block says so, so an edited body cannot silently show the abandoned draft.
  • src/repos/github.ts — PullRequestInfo gains body, and pullRequestDiffTotals reduces the existing changed-file walk to { files, additions, deletions }. A capped walk reports no totals instead of a total from part of the change; that is the same rule the path guard already states.
  • src/app.ts — the job route fills the block from one read of the pull request, cached per job for 60 seconds (the shape inboxCache already uses), so a refresh spends no GitHub request. A job with no pull request skips the read entirely. A read that fails — 404, no token, a capped list — is caught, logged without request data, and leaves the page at 200 naming the unavailable fact.
  • src/views/pages.ts — the block, bilingual, every interpolated value escaped. The body text is data on this page, never markup.
  • test/changeStory.test.ts — 11 cases: the three sections, a missing heading, a duplicate heading, an empty body, the diff sum, a capped list, an unreadable pull request, the bare Ran: form with Not run: not swallowed, plus route cases for the rendered block, the 404-that-stays-200 and the no-PR job that spends no request.

Anchors, re-read at the base this branch uses

The card's anchors were read at 6287aa53. I re-read them at 183779b8 and then based the branch on live main 0c05df87, as instructed. What moved:

  • jobPage() — src/views/pages.ts:2712 (card said :2702).
  • The evidence rows — src/views/pages.ts:2844 and following (card said :2834-2843). The new block renders above that section, not inside it.
  • The job route — src/app.ts:3001 (card said :2905); it moved under #771, #773 and #764.
  • PullRequestInfo — src/repos/github.ts:51 (unchanged); listPullRequestFiles — src/repos/github.ts:817 (card said :796).
  • The cache precedent, inboxCache — src/app.ts:814 (card said :810).

Nothing else in the card's scope changed shape.

Scope and tier

8 files, +557/−2. src/lib/changeStory.ts, src/repos/github.ts, src/app.ts, src/views/pages.ts, test/changeStory.test.ts, docs/api.md, docs/architecture.md, changelog.d/373-change-story.added.md. No workflow, no docker-compose.yml, no .github/ change, no CSS change (the block reuses the existing pre-wrap class for the body text).

Slices 2 and 3 are untouched: no alternatives-considered / uncertainty / attribution sections, no packet hash, no decision-time metrics. No zh/en wording pass beyond the block itself and no 360 px pass, per the card.

Ran:

  • npx tsx --test test/changeStory.test.ts at this tip — 11/11 pass.
  • npm run check at this tip (ab68fc49) — pass: lint, typecheck, 1999/1999 tests, build.
  • npm run lint at this tip — exit 0: lint: 11 warning(s) in 8 entries match the baseline (the ci: fail on new lint warnings instead of allowing the current seven #653 gate; no new warning, no baseline edit).
  • node scripts/risk-gate.mjs --base origin/main --head HEAD — risk-tier: T3; basis: src/repos/github.ts.
  • node scripts/version.mjs check — version: 0.3.6 ok; node scripts/changelog.mjs check — 41 fragments ok.

Not run:

  • CI at this tip — the pull request was opened just now; the run id and conclusion follow in a comment once the run exists.
  • Docker build and any deploy: nothing here touches the image, the compose file or a workflow.
  • No live instance: the block was exercised through the route tests with a fake GitHub server, not against a running server and not against the live database.

Red half at the base

test/changeStory.test.ts on the unbroken tree (0c05df87 plus only that test) is red before the code exists: ERR_MODULE_NOT_FOUND .../src/lib/changeStory.js, 0 pass / 1 fail. The behaviour half is covered by the route cases: on the unbroken tree a job page has no Why this change block at all, and the first test asserts its absence as well (assert.doesNotMatch(page.body, /Why this change/) in the no-PR case).

risk-tier: T3; basis: src/repos/github.ts (T3 by rule); src/app.ts, src/lib/changeStory.ts, src/views/pages.ts are T2, test/changeStory.test.ts T1, docs and the fragment T0; the highest wins.
Docs: docs/api.md (the new body field and the totals helper), docs/architecture.md (the block, the 60 s cache and the pure parser).
Changelog: changelog.d/373-change-story.added.md.
Rollback: revert this one commit. The route stops calling the helper, the block disappears, and PullRequestInfo.body becomes unused — no schema change, no data migration, nothing to clean up.
maintainer_can_modify: true — the branch is in this repository.

@HugoXK

HugoXK commented Nov 15, 2026

Copy link
Copy Markdown
Owner Author

Written-by: HugoXK
Author: Fledge Platform Dev-1

Green at the tip ab68fc49d07734dd0aa1ff260430ca94527399a8:

  • ci-ok success — run 43688332407, https://github.com/HugoXK/fledge/actions/runs/43688332407
  • lint, typecheck, test (24), coverage, build, dependency-review, changelog, secret-scan, version, risk, docker, codeql-required, visual, CodeQL: success
  • workflow-lint, action-pins, restore-drill, audit: skipped, because the change classifier sees no workflow, data or deploy path
  • pr-lint success

review-gate is red for one reason and it is the expected one at a T3 tip: no PASS names this commit yet. That is the Reviewer's half, not a finding.

Red half at the base 0c05df87, for the record: test/changeStory.test.ts on the unbroken tree fails with ERR_MODULE_NOT_FOUND .../src/lib/changeStory.js, 0 pass / 1 fail, and the block does not exist in the rendered page (assert.doesNotMatch(page.body, /Why this change/) in the no-PR route case).

One correction for the record: maintainer_can_modify reads false on this pull request only because the branch lives in this repository, where maintainers already have write. Nothing about the branch is locked.

@HugoXK-bot HugoXK-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: PASS at ab68fc4
Written-by: HugoXK-bot
Author: Fledge Platform Dev-1
Posted-by: HugoXK-bot

Head ab68fc49, tree 126c9b0c4c35a67003b35d122a90fb10ce95451f, base 0c05df87, 8 files +557/−2, risk-tier: T3; basis: src/repos/github.ts (recomputed here, not taken from the body).

Ran:

  • npm run check at ab68fc49 — exit 0: lint 0 errors, typecheck clean, 1999/1999 tests, build ok
  • node scripts/risk-gate.mjs --base 0c05df87 — risk-tier: T3; basis: src/repos/github.ts
  • check-runs at the head, newest per name: ci-ok success (07:07:30Z), build, coverage, test (24), lint, typecheck, docker, visual, changelog, version, risk, secret-scan, codeql-required, dependency-review success; pr-lint success on both of its runs; review-gate the only failure, expected before a verdict
  • rendered jobPage at this tip in Chinese with a real readChangeStory result and confirmed 为什么有这个改动, 改动了什么, 风险, 运行了什么, 改动规模 and 2 个文件 +5 −1 all appear
    Not run: a live page against a real GitHub pull request (the route tests use a fake server), the 360 px layout, the Docker image.

Escaping, the part that matters on a page that prints a pull-request body

The body text is carried as data end to end. readChangeStory/parseChangeStoryBody produce plain strings, changeStoryFacts never marks them up, and the page escapes every interpolation in the row — label, value and note (src/views/pages.ts:2873): <small class="job-report">${escapeHtml(fact.value)}</small>, note likewise, heading and labels likewise. class="job-report" is the existing white-space: pre-wrap rule (public/app.css:272), so a multi-line Ran: block keeps its shape without new CSS. The test at test/changeStory.test.ts:118-160 drives a real <b>bold</b> through the route and asserts &lt;b&gt;bold&lt;/b&gt; in the HTML and the absence of the raw tag, so this is pinned rather than asserted in prose. Bilingual strings are present for all four facts, the heading, and the four states (not stated/未说明, not available/读不到, the capped sentence, the duplicate note).

Slice 1 stayed slice 1

The module parses exactly three sections and a diff total (src/lib/changeStory.ts:8, :55-60) — no alternatives-considered, uncertainty, attribution, packet hash or decision-time metrics anywhere in the diff, and no CSS file touched. The absent-heading rule is the one the card asked for: a section the body does not carry is null and the page says not stated rather than borrowing another section (:133-136, :167); a repeated heading keeps the last one and is flagged, so an abandoned draft cannot silently win (:99-105, :168). The bare Ran: form this repository's own template uses is read, and a Not run: line ends the section instead of being swallowed into it (:71-82, pinned at test/changeStory.test.ts:110-116).

Failure paths

A pull request read that 404s, a missing token, an unparseable pr_url (the origin rule in parsePrUrl, src/jobs/prUrl.ts:93) and a capped file walk all land in readChangeStory with an explicit state and leave the page at 200 (src/app.ts:846-868); the capped walk reports no total at all rather than summing part of the change (src/repos/github.ts:857-869), which matches the rule the path guard already states. A job with no pull request returns before any request is made (src/app.ts:845), and the 60 s per-job cache keeps a refresh free.

Non-blocking: the cache cleanup at src/app.ts:858-860 only deletes expired entries once the map holds 200, so within one TTL window the map can exceed the cap and simply keeps the entries; a delete of the oldest or of the first key when full would make the bound exact. Not worth a round on its own — it is bounded by job pages viewed per minute, not by anything a user controls.

Conclusion: the block is read from one place, every value reaching the page is escaped, both languages are present, the failure states are honest, and the change is visibly slice 1 rather than the whole card.

@HugoXK-bot HugoXK-bot added the review:approved Issue reviewed and approved by the Fledge Platform Reviewer label Nov 15, 2026
@HugoXK
HugoXK merged commit 8f69d1e into main Nov 15, 2026
33 of 34 checks passed
@HugoXK
HugoXK deleted the dev1/373-change-story-slice1 branch November 15, 2026 07:22
HugoXK added a commit that referenced this pull request Nov 15, 2026
Rebuilt as a single-parent commit on live main 8f69d1e (no base-absorbing
merge commit, per AGENTS.md). Resolutions against main: package.json keeps
both `evolution:consent` and `lint:update`; docs/architecture.md keeps #783's
content and carries the window wording fix (the manifest window is anchored on
the newest exported row, the CLI query bound is wall-clock now - days).
src/, test/ and scripts/ are byte-identical to ee0f8f9.
Slice 1 of #373. A job page proved the checks passed but not what the change
was, so a maintainer had to open the pull request to learn it. The page now
opens with a "Why this change" block above the evidence rows:

- `src/lib/changeStory.ts` (new, pure) splits the pull request body on its
  `## What changed` / `## Risks` / `## Ran` headings (any level, plus the bare
  `Ran:` line this repository's template uses). A heading the body does not
  carry renders `not stated` and is never filled from another section; a
  repeated heading keeps the last occurrence and the block says so, so an
  edited body cannot silently show the abandoned draft.
- `PullRequestInfo.body` carries the body the agent already wrote, and
  `pullRequestDiffTotals` reduces the existing changed-file walk to
  `{ files, additions, deletions }`. A capped walk reports no totals rather
  than a total from part of the change, the same rule the path guard states.
- `GET /repos/:id/jobs/:jobId` makes one cached read per job (60 s) to fill the
  block; a job with no pull request reads nothing at all. A read that fails
  leaves the page at 200 and names the unavailable fact.
- The block is bilingual and every value is escaped; the body text is data on
  this page, never markup.

Docs: docs/api.md (the new reader field and totals helper), docs/architecture.md
(the block, the cache and the pure parser). Changelog: one `added` fragment.

Fixes #373

Written-by: HugoXK
HugoXK added a commit that referenced this pull request Nov 15, 2026
Rebuilt as a single-parent commit on live main 8f69d1e (no base-absorbing
merge commit, per AGENTS.md). Resolutions against main: package.json keeps
both `evolution:consent` and `lint:update`; docs/architecture.md keeps #783's
content and carries the window wording fix (the manifest window is anchored on
the newest exported row, the CLI query bound is wall-clock now - days).
src/, test/ and scripts/ are byte-identical to ee0f8f9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved Issue reviewed and approved by the Fledge Platform Reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: make the review packet explain the change, not just prove the checks

2 participants