From 62f21d847bd4e565fc04465b986739abfb88d58f Mon Sep 17 00:00:00 2001 From: Chris0Jeky Date: Fri, 4 Sep 2026 06:59:29 +0100 Subject: [PATCH] Record the eighth 2026-09-04 status block --- docs/STATUS.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/STATUS.md b/docs/STATUS.md index 55bae17d4..3ecb34a53 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -109,6 +109,27 @@ Backend: - **Proposals record the provenance producer triple and expose it through a board-authorized endpoint (`#1987` backend half, PR `#2494`, merge `f5b3225ec`).** `ProposalProvenance` gains nullable `Provider` and `PromptVersion` (64 chars, blank normalized to null, over-long rejected at the domain boundary) under migration `20260904030926_AddProposalProvenanceProducerTriple` — two additive nullable `TEXT` columns, no rebuild and no backfill. `CreateProposalDto` carries the two values as `[JsonIgnore]` init-only properties on the same trusted-input pattern as `TrustedConfidence`, so a client cannot label its own proposal's producer; only `CaptureTriageService` sets them, from the provider that actually ran or the deterministic extractor on fallback, and `BuildCreationProvenance` stamps them server-side. New `GET /automation/proposals/{id}/provenance/metadata` shares the exact board read authorization and 404 parity of the sibling `{id}/provenance` route. The contract fails closed: `Provider` is null when no producer was recorded, `Model` and `PromptVersion` are reported only alongside a recorded provider so an origin sentinel (`chat-tools`, `manual`, `queue`) can never render as a model name, and a proposal with nothing recorded returns 200 with all-null fields. This addresses states 3 and 4 of `#2315`; states 1 and 2 stay open there. Evidence: `ProposalProvenanceTests` 20/20, 428 focused Application tests, the new endpoint's 5 API tests plus `AutomationProposalsApiTests` 56/56 and `MigrationBootstrap` 22/22, Architecture 28 with the pre-existing skip, `has-pending-model-changes` clean, docs governance; the reviewer applied an authz/trust/migration lens and found the endpoint byte-identical in gating (owner and collaborator 200, stranger parity including a body-leak assertion, missing id 404) and the `[JsonIgnore]` properties unreachable from the single `[FromBody]` site. **No frontend change here** — the `usePaperReviewSelectors.ts` source swap is deliberately deferred to avoid colliding with the in-flight Review race-state work, so `#1987` and `#1284` AC4 stay open. MEDIUM plus two LOWs on `#2499`: pre-migration LLM rows with a real `ModelId` but null `Provider` now render silent, and only capture triage stamps the triple. - **Not shipped reality:** two Smart CI shadow reds in this range — on PR `#2485`, and again on the still-open `#2496` and `#2500` — are the same planner base-mismatch shape recorded on `#2327`, not a verdict on those branches; PR `#2506` is open as the fix. Open and unmerged at the time of writing: PRs `#2484` (whose review filed `#2501`), `#2496`, `#2497`, `#2498`, `#2500`, `#2502`, `#2503`, `#2505`, `#2506` and `#2507`. Their state lives on the PRs and their issues, and `docs/releases/V0_3_0_READINESS.md` carries the release-gate view. +v0.3 integration wave, eighth block (2026-09-04, `main` `65c77a53f` to `c174a517a`; overnight orchestrator lane continued, thirteen merges). Codex review credits stayed exhausted (SC-9) for this whole range too, so **every PR below carried one fresh-context independent reviewer subagent instead of a Codex outcome**, and the connector's usage-limit notice was classified once per PR as informational rather than a finding. Control-plane PRs `#2502`, `#2506` and `#2522` were deliberately **not** merged by the orchestrator: under ADR-0066's 2026-09-03 amendment they wait for the maintainer's review, queued as human-action item SC-10: + +Backend and platform authorization: +- **The development sandbox never widens write-class authorization (`#1866`, PR `#2498`, merge `17e48815e`, ADR-0068).** The sandbox branches are deleted from `AuthorizationService.CanWriteBoardAsync`, `GetWritableBoardIdsAsync`, `CanDeleteBoardAsync`, `CanManageBoardAccessAsync` and `GetUserRoleForBoardAsync` (which now reports the caller's real role instead of a synthetic `Owner`), from `BoardAccessService.EnsureCanManageBoardAccessAsync` and from `ProposalExecutionAuthorizationSnapshotReader`; the flag keeps its **read** bypasses and stays a *requirement* for the export/import endpoints. This closes the divergence where a sandbox `Viewer` passed the API bar and was then refused at the worker, which `AutomationPolicyEngine.HasAccessAsync` never had a branch for. Fixtures now seed real `BoardAccess` rows; no test logic was relaxed. Evidence: 123 focused Application tests, 57 Api tests, Architecture 28 with the pre-existing skip, docs governance; a round-2 docs pass reconciled `BETA_THREAT_MODEL.md` residual 4 and the `TESTING_GUIDE.md` sandbox section with the shipped posture (19 `BoardAccessServiceTests` re-run). Not verified: no full backend suite. Two flag branches stay open on `#1866` — `LlmQueueService:133` (cross-user request ownership) and `BoardJsonExportImportService:239` (a read check). +- **Transcript date resolution is anchored to the capture, not the triage day (`#2193` partial, PR `#2515`, merge `49ace6799`).** The new typed `CaptureTriageAnchor` carries the server-stamped capture instant plus **only** the UTC offset the capture already reported, and is threaded explicitly through `ICaptureTriageService.CreateProposalFrom{Capture,Transcript}Async` and `ILlmCaptureTriageExtractor.ExtractAsync`; both production workers build it from the queue row's `CreatedAt`, one anchor serves the first attempt, every map chunk and every retry, and the `#2206` plausibility window is measured from the same value. No new client-trusted DTO field, and the prompt text is unchanged. Evidence: red-first (the five new extractor tests fail against the old anchoring), 184 Application capture-triage tests, 327 Api `Capture|Triage|LlmQueue` tests, Architecture 28/1 skip, a clean Release solution build; the reviewer bounded the client-controllable shift at ±1 calendar day of the attacker's own capture and confirmed both production entry points pass a real anchor. This PR also corrected the stale `docs/STATUS.md` reference-date sentence in this file, which had said the reference date was the server's UTC day at triage. `Refs #2193`, not `Closes`: surfacing the dropped-date parser notes to the reviewer and the `llm-triage.v3` prompt-version migration (`#2211`) remain open there. +- **Unicode line and paragraph separators are stripped from log output (`#1652`, PR `#2497`, merge `63cb8754d`).** The shared log sanitizer now removes U+2028 and U+2029 in the single predicate every log wrapper delegates to, with query, telemetry and unhandled-exception regressions extended. Evidence: the focused Application and Api filters green; the reviewer confirmed sanitization happens only inside logger argument lists (no stored data mutated) and that strip-then-truncate composes with the still-open surrogate-safe truncation on `#2500`. Accuracy note recorded on the PR: U+0085, U+000B and U+000C were already stripped by the pre-existing C0/C1 ranges, so the new behaviour is U+2028/U+2029 only. Residuals outside the diff (a raw token prefix logged by `ApiKeyMiddleware`, bidi/zero-width characters, exception messages bypassing the sanitizer) are tracked on `#2519`. +- **Expiry-sweep coverage is tightened and its archived-board reporting aligned (`#2209`, PR `#2505`, merge `0c9ef2219`).** The archived-board skipped count is pinned to the exact fixture delta, notification and empty-log assertions are tightened, the service lane's archived-board reporting matches the worker's transition-only policy, and the telemetry exclusion is documented. Evidence: 160 Application and 37 Api tests, docs governance; the reviewer confirmed the service-lane change is log-level only with no production caller and the returned count unchanged. Follow-up `#2520` carries two MEDIUMs (a transition-dedupe field on a DI-scoped service, and a STATUS sentence that overstated it) and three LOWs. + +Frontend: +- **Retained Inbox rows survive a same-scope refresh failure (`#2044`, PR `#2484`, merge `bfe8daa09`).** The orchestrator distinguishes a route-scope replacement load from an ordinary refresh: retained rows and their count stay visible alongside a retryable same-scope error, and are hidden while a replacement scope is loading or failed. The review's HIGH was real and fixed — `onMounted` called `loadInbox()` rather than `loadInboxForScopeReplacement()`, so re-entering Inbox at a *different* board with a failing first load rendered the previous board's rows under the new board's scope label; mount is now unconditionally a scope replacement. Evidence: 183 targeted tests across the orchestrator, `PaperTriageTable` and `PaperInboxView` specs including a lifecycle case proven red without the fix, typecheck and scoped ESLint. Not verified: no full vitest run and no E2E. Two MEDIUMs and one LOW (a discarded-response race clearing `isScopeReplacement`, the header eyebrow counts ignoring scope replacement, no in-flight affordance) are tracked on `#2501`. +- **Cancelling a pending Review edit returns focus to the control that opened it (`#2457`, PR `#2513`, merge `1d768caaa`).** The Request-edit control is captured before the authoritative revision metadata is awaited and focus is restored to the same proposal's control when the pending open is cancelled or its metadata load fails; pending focus state is invalidated on proposal switch, read-only transition and unmount. Evidence: `PaperReviewView.spec.ts` 156 passed, typecheck, scoped ESLint; the reviewer traced the single-consumption capture and by-value epoch comparison, and confirmed the `#2423` save-focus path cannot fight this one. Three LOWs declined, including that the detached-element fallback branch is not exercised. +- **Paper Review renders the recorded provenance triple from the proposal endpoint (`#1987`, PR `#2511`, merge `892d6c74e`; closes `#1987`).** The frontend half of the backend contract landed in `#2494`: `proposalDeepReviewApi.getProvenanceMetadata` reads `GET /automation/proposals/{id}/provenance/metadata` and becomes the **primary** source for the footnote and drawer, with the `#2310` capture-detail path kept only as a fallback when the endpoint recorded nothing and the proposal carries a capture link. The read is added into the existing `Promise.allSettled`, inheriting the current generation/abort guards; it stays strictly optional (outside the six Apply-gating selectors) and fails closed — a null provider, a normalized 403/404, or any other failure all render no claim. Evidence: 212 focused tests plus the whole `views/paper/review/` directory at 313 passed with 0 unhandled errors (identical to a stashed pre-change baseline), typecheck, build, ESLint on all eight changed files; the reviewer confirmed no synthesized producer claim is possible and that every abort bumps the fetch generation first. Caveat on `#2499`: only capture-triage proposals carry the triple today, so Chat/Manual and pre-`#2494` proposals still render no claim — correctly, but silently. A MEDIUM (a 403 on the proposal read normalized to "unrecorded", letting the owner-only capture fallback answer) is tracked under `#2315` states 2/3. +- **Keyboard-shortcut notation feature-detects the platform with a last-resort legacy fallback (`#2008`, PR `#2525`, merge `c174a517a`; closes `#2008`).** `formatShortcut` now resolves in three tiers — `navigator.userAgentData.platform`, then `navigator.userAgent` when it names any recognised platform, then a `typeof`-guarded `navigator.platform` consulted only when the first two are silent or generic — so a reduced browser exposing only `MacIntel` renders `⌘` while a reduced Windows/Android/Linux UA stays decisive on Ctrl. The source-literal AC is closed too: `PaperInboxView.vue` passes the canonical `kbd="mod+;"` (rendered output unchanged) and the `useReviewKeymap.ts` glyph comment is reworded, emptying the shortcut-notation guard's `RELEASE_LANE_QUARANTINE` so the guard is now strict. Evidence: 86 tests across four specs with one case per tier boundary, typecheck, `npm run lint` at 0 errors and 8 pre-existing unrelated warnings. Not verified: real macOS/Windows hardware and a real legacy browser — every tier is exercised through injected navigator hints. + +Guards, tests and docs: +- **Three accessibility lint rules are enforcing, the anchor guard covers its named residuals, and AC3 has a mechanized helper (`#1949` slice, PR `#2514`, merge `bc4723144`).** `vuejs-accessibility/interactive-supports-focus`, `click-events-have-key-events` and `no-static-element-interactions` move from `warn` (under `--max-warnings=20`, i.e. advisory) to `error`, measured at **0 violations across all 177 SFCs first**, so no site is allow-listed. The dead-anchor guard now detects `href=""` and `href="javascript:void(0)"` in static and bound-fallback form — neither shape exists in `src/` today, so this is pure regression prevention — and a new `expectGuardedPrimaryAction` helper plus an opt-in registry mechanize AC3 (a primary action with an unmet precondition must be disabled or must tell the user; silence fails), with two actions registered. AC3 is deliberately not a repo-wide scan. Evidence: guards 27 passed / 4 files, Paper inbox 115 passed, typecheck, `npm run lint` 0 errors, a JSON-formatter violation census for the three promoted rules, and red-first proof for both new detectors; round 2 fixed a confirmed MEDIUM in the helper (a shallow-spread emit snapshot aliased VTU's live arrays, so a control that had already emitted `error` would have produced a false failure) and is now counted rather than spread. `Refs #1949`, not `Closes` — the briefed static rule set was already shipped by earlier PRs, and this slice delivers what was genuinely missing. +- **The board-archive smoke journey waits for the archive DELETE (`#2163`, PR `#2507`, merge `6c9d3b876`).** The response waiter is armed before the archive click and its success asserted, preserving `BoardSettingsModal`'s navigation-before-mutation ordering from `#519`, before the test's hard navigation to Archive. Test-only; the broader reload/tab-close durability question is explicitly out of scope. Evidence: the scoped Playwright case 1 passed and 2 passed under `--repeat-each=2`, scoped ESLint; the reviewer confirmed `DELETE /boards/{id}` returns 204 and that the modal's `router.push` is an SPA transition, so the DELETE cannot be aborted. Three LOWs declined. +- **The standalone MCP host's preflight behaviour is pinned (`#1637`, PR `#2503`, merge `8962b9ada`).** New tests over the real host lifecycle assert the bare `204` preflight carries no `Access-Control-*` headers and bypasses API-key authentication, and the per-key limiter environment value is formatted with invariant culture. Evidence: 9 `StandaloneMcpHostFilteringTests` passed against a real Kestrel host; the reviewer confirmed the short-circuit fires only for `OPTIONS` with `Access-Control-Request-Method` while GET/POST still traverse `ApiKeyMiddleware`, and corrected the boot-path accounting to four. Three LOWs declined. +- **Docs only:** PR `#2509` (merge `9e431ab02`) records the seventh block above. PR `#2512` (merge `e88e5e513`) adds human-action item SC-10 to `OUTSTANDING_TASKS.md` §J — the control-plane PRs prepared but not merged under ADR-0066's amendment (`#2502`, `#2506`, the `#1898` dev-up port-release work, and the CI-07 `#2331` proposal) — and discloses post hoc that `#2479` was merged with the fresh-context review but without the maintainer's review. It records open items; it checks nothing off. +- **Not shipped reality:** PR `#2496` (`#2434`, clearing a stale board-load error after background recovery) was reviewed SHIP and merged, but into its stacked base branch `issue-2430/board-mutation-alert` **after** that base had already landed as `#2495`, so merge `c60d0156a` is **not** an ancestor of `main` and the fix has not shipped; its owner must re-target it. Its review filed a MEDIUM on `#2523` (the new spec's success mock clears the store error itself, so the view branch is untested). `main`'s first completed CI run of the night, at `17e48815e`, was red only on the Windows launcher regression suite — the `#2378` shape recorded there — with every other required leg green. Open at the time of writing: PRs `#2500`, `#2502`, `#2506`, `#2510`, `#2516`, `#2517`, `#2518`, `#2521`, `#2522`, `#2527` and `#2528`; `#2527` (`#2141`, a transcript source option in the Paper capture composer) was still open when this block was written. Their state lives on the PRs and their issues, and `docs/releases/V0_3_0_READINESS.md` carries the release-gate view. + v0.3.0-rc.1 SHIPPED (2026-08-30, annotated tag `3fc9f6e8e` peels to `9d2ea3c7c`): - **The public v0.3.0-rc.1 pre-release exists**, cut by the agent under the maintainer's v0.3 RC deck reply q-1 A (2026-08-30; map `map:v1:bec0a8dd…dd9138`; record `#1947`) and the repository's declared authority. The GitHub Release is `prerelease=true`, `draft=false`, published 2026-08-30T02:26:06Z with three assets — `taskdeck-v0.3.0-rc.1-win-x64.zip` (53,916,746 bytes), its `.sha256` sidecar, and `taskdeck-v0.3.0-rc.1-provenance.txt` — and a composed page (download badge first, RC banner, SHA-256, quick-start link, `## Breaking changes` lifted from UPGRADING, `## Highlights` from `docs/releases/notes/v0.3.0-rc.1.md`, grouped `## What's changed`). `/releases/latest` still resolves to `v0.2.0`. - **Tag workflows:** CI Release 33287786328, Release Security 33287786318, Release Container 33287786267, Release Desktop 33287786253 — all four success. **GHCR:** `ghcr.io/chris0jeky/taskdeck:0.3.0-rc.1` published (`sha256:d47bdf2d…2db67`), `latest` and `0.2` both still `sha256:e4915d72…8c752`, and no `0.3` alias exists — the floating `latest` / `0.2` index digest is unchanged from the pre-tag capture (`sha256:e4915d72…8c752`) and no `0.3` alias was created, which is the live proof of `#2217`/PR `#2223` that the threat-model row was waiting for.