Skip to content

0.3.0 qualification: dev → unstable - #674

Merged
mberrys merged 12 commits into
unstablefrom
dev
Sep 22, 2026
Merged

mberrys merged 12 commits into
unstablefrom
dev

Conversation

@mberrys

@mberrys mberrys commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Purpose

Promote the current assembled dev state into the 0.3.0 qualification branch.

This PR is intended as dev → unstable, not a release-to-stable PR. It is the transport for exact-candidate qualification after the 0.3.0 foundation promotion in #670 and the subsequent architecture/security changes merged through #672 and #673.

Current role

The PR remains draft until the P0 queue in #638 is closed or explicitly re-scoped in GitHub.

Candidate contents

The current assembled diff includes architecture/proof-lane machinery from #672, image-decode hardening from #673, and current preflight/conformance work. Qualification applies to the assembled head SHA, not to topic PRs independently.

Required before Ready for Review

Promotion rule

After qualification and merge to unstable, use a separate unstable → stable promotion PR that names the exact qualified SHA and references #638. Do not use topic-branch evidence as a substitute for assembled-candidate proof.

cursoragent and others added 11 commits September 21, 2026 23:32
…orted (#665)

Loop does not produce or validate PDF/X-5n, PDF/X-5g, or PDF/A-3. A document
that declares one of those levels is reported unsupported instead of passing
that class in silence.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Co-authored-by: michael berry <mberrys@users.noreply.github.com>
…t produced (#665) (#671)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## What changed

Loop does not produce PDF/X-5n, PDF/X-5g, or PDF/A-3. Core has no
external-resource or n-colorant output-intent rules for those levels, so
this is not a converter and not a renamed PDF/X check. A document that
declares one of those levels used to pass with nothing looking at the
claim. The new `conformance-claims` check parses the declaration once
and reports check status `unsupported` (reason
`unsupported-conformance-level:<ids>`), which leaves the run incomplete.
A file that does not declare them gets no finding. PDF/VT stays on #605.
Refs #665.

Protected paths: `LoopLibCore/sources/pdfconformanceclaim.h`,
`LoopLibCore/sources/pdfconformanceclaim.cpp`, and
`LoopLibCore/sources/preflightengine.cpp`. The claim table and the check
belong in Core and do not depend on Widgets. No report schema or central
type was added; `unsupported` is already a check status.

## Release changelog

<!-- Promotion and release PRs (`dev` -> `unstable` -> `stable`) only:
the full changelog goes here, shaped by `docs/CHANGELOG_FORMAT.md` -
measured scope line, Added/Changed/Fixed/Security/Internal, one `Closes
#<n>` line per issue whose acceptance criteria the release meets, `Refs
#<n>` plus the outstanding criterion otherwise, verification naming
workflows and SHAs, and a stated breaking-change answer. Topic PRs
answer with their `changes/<branch>.md` fragment and leave this comment
in place. -->

## Proof

- [x] `python scripts/agent/check-change.py --base origin/dev
--build-dir build` reports `pass` (head
`b2c56079109a5c5728f2931173f9416c8cefa35e`, 71 checks)
- [x] One `changes/feat-0.3.0-665-pdfx5-pdfa3-claims.md` fragment added
(Category, Audience, Breaking-Change, Summary)
- [x] Changed behaviour has a test that fails without the change
(`UnitTestsPreflightEngine`, 110 passed, including declared PDF/X-5n,
PDF/X-5g, and PDF/A-3 versus PDF/X-1a, PDF/X-4, PDF/A-2, and PDF/VT)
- [x] Protected-path or contract change named above, with the reason it
is required

## Internal logic (touched behavior-bearing code)

- [x] Guard clauses handle invalid, stale, cancelled, absent,
unauthorized, and terminal cases before the happy path
- [x] Untrusted input is parsed once at the boundary into trusted typed
or domain state, with no repeated checks downstream
- [x] Invalid state stops before partial mutation or publication and
returns a descriptive error or result
- [x] Names carry the domain intent, and comments explain rationale
rather than restating the code

## Anti-slop pass

- [x] Redundant or explanatory comments that do not match the file's
style removed
- [x] Abnormal defensive checks and broad try/catch blocks removed where
a trusted upstream boundary already guarantees the invariant, with real
boundary and safety checks kept
- [x] No `any` or equivalent cast added only to suppress a type error
- [x] Python imports stay at file scope unless a local import is
required
- [x] Generated boilerplate, needless wrappers, and local-style drift
removed
- [x] Validation, security, cancellation, provenance, and failure
handling preserved

Anti-slop summary (1-3 sentences):

The claim table is the only place production and validation are
recorded. Metadata is parsed once into level ids, and undecodable
catalog metadata stays `not_inspected` rather than `ok`. The profile
severity is not promoted to an error, because a declared level is
outside the validator.

## Security and rollback

- [x] Untrusted input validated at the trust boundary; no new unsafe
construct without an inline justification
- [x] Rollback: revert the branch. The check is opt-in by profile id
`conformance-claims`; the default profile is unchanged.

## Docs

- [x] Docs updated in this PR, or "none needed" with the reason

## Self-review (BSP-002 §4.3)

- [ ] Reviewed in the diff view, not the editor, at least 30 minutes
after the final commit; overnight if the change touches
security-sensitive code, data handling, or public API surface

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-9ed7e283-8e24-5230-b9cb-3763b3599a73?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-9ed7e283-8e24-5230-b9cb-3763b3599a73&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/studio-berry/codesmith/loop/pr/671?ref=codesmith_pr_footer"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1792626384&installation_model_id=435800&pr_number=671&ref=codesmith_pr_footer&repository=studio-berry%2Floop&return_to=https%3A%2F%2Fgithub.com%2Fstudio-berry%2Floop%2Fpull%2F671&signature=2612d9c245c58e874184335effd2afc95cc2f64f26d3911412985b828e4edc3e"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
Record proof lanes, boundaries, fixture classes, and quality budgets
as architecture contracts. CI and check-change fail when a mapped
subsystem lacks a lane or a required budget, and when a PR evidence
manifest omits those bindings.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
agent-fast failed because the architecture contract suite imported
PyYAML, which CI does not install. The subset loader stays the
contract parser; the parity check runs only where PyYAML is present.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Reject short sample buffers and reserve render pixels before QImage
or codec output buffers so hostile image XObjects cannot bypass
PDFProcessingBudget. Shared guard covers DCT, JPX, CCITT, JBIG2, and
raw paths; scan id 43899b1.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Keep sample-length and codec dimension guards in createImage, but charge
render pixels only when painting actually rasterizes. Avoid aborting
Colorants evidence walks with BudgetExceededException from image Flate.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Unblock agent-fast format checks on the protected PDF image paths.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
Register UnitTestsImageDecodeGuard so source_integrity and policy
evidence checks stay current with UnitTests/CMakeLists.txt.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## What changed

Hostile PDF image XObjects could allocate a full raster (or codec output
buffer) before `PDFProcessingBudget` ran. This PR adds a shared
`PDFImageDecodeGuard`, rejects short sample buffers / out-of-cap codec
dimensions before allocation, and reserves render pixels before
`getImage` on the paint path. Metadata-only walks (color-mode /
Colorants evidence) that short-circuit paint do not charge pixels. Scan
revision `43899b19` (`pdf-image-decode-budget-bypass` + related image
codec deferred items). Attachment / launch / export candidates dismissed
in store triage with citations.

## Release changelog

Topic PR — see `changes/cursor-security-image-decode-budget-a16c.md`.

## Proof

- [x] `python3 scripts/agent/check-change.py --base origin/dev`: focused
tests + builds **pass**; `format` incomplete (`clang-format` missing in
VM); `clang_tidy` fails resolving Qt/system headers in this environment
(not a new logic diagnostic)
- [x] Changelog fragment
`changes/cursor-security-image-decode-budget-a16c.md`
- [x] `UnitTestsImageDecodeGuard` covers truncated max-dimension images,
low `maxRenderPixels`, small-image regression, excessive CCITT columns
- [x] Protected paths: `pdfimage.*`, `pdfcolorspaces.cpp`,
`pdfpagecontentprocessor.cpp`, new `pdfimagedecodeguard.*` — required
for the decode boundary

Test log:
[walkthrough-image-decode-guard-tests.log](https://cursor.com/artifacts/c/art-beb3982b-b9bb-4dd6-85bb-46fedd59d753)

## Internal logic

- Guard clauses reject invalid geometry / short samples before raster or
codec buffers
- Untrusted Width/Height/BPC/Columns validated once at the decode
boundary
- Failures throw before `QImage` / large `QByteArray` materialization

## Anti-slop pass

Anti-slop summary: Kept one shared guard module instead of per-codec
booleans; left stream session-budget threading open because budgeting
Flate in `createImage` aborts Colorants evidence walks via
`BudgetExceededException` (hard STREAM_FILTER ratio still applies).

## Security and rollback

- Untrusted image dictionaries validated before allocation; render-pixel
reserve before paint rasterization
- Rollback: revert this branch / PR

## Docs

- none needed (behavior hardening; triage lives in agent store
`internal/codex-security-scan-43899b19/`)

## Self-review

- Diff reviewed before opening this draft PR
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-2ed3ae12-5034-50bc-afe8-c05bdc89a16c?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-2ed3ae12-5034-50bc-afe8-c05bdc89a16c&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/studio-berry/codesmith/loop/pr/673?ref=codesmith_pr_footer"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1792653141&installation_model_id=435800&pr_number=673&ref=codesmith_pr_footer&repository=studio-berry%2Floop&return_to=https%3A%2F%2Fgithub.com%2Fstudio-berry%2Floop%2Fpull%2F673&signature=5b02fb46b1b57bd4fac7a5b20dd7ecfee34519b8fa5571eb66277cd4d9fbaa51"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## What changed

CI now treats architecture as a contract, not a narrative.
`architecture/` records boundaries, invariants, proof lanes, ownership,
fixture classes, and quality budgets.
`scripts/agent/check-architecture.py` loads those files on every
`check-change` run and on the source-integrity jobs. A pull request that
touches a mapped subsystem other than documentation must ship
`changes/<branch>.evidence.yaml`. Claims can cite `budget:<id>`, and
that id must exist in `architecture/quality-budgets.yaml`.

Quality budgets detect movement. Four Loop metrics are measured against
committed baselines (preflight PDF count and canvas-parity caps).
Latency, memory, parser agreement, packaging size, and the sealed
rendering cap are declared with harness TODOs, and the mapped
visual/data subsystems are still required to have a budget.

## Release changelog

`changes/cursor-proof-lanes-806f.md`

## Proof

- [x] `python scripts/agent/check-change.py --base origin/dev` reports
`pass` (head `1bf13aa3d39c7d6c9d80e32e14fb762829049fe1`; no
`--build-dir` because the selected module is documentation and no C++
target is in the diff)
- [x] One `changes/cursor-proof-lanes-806f.md` fragment added (Category,
Audience, Breaking-Change, Summary)
- [x] Changed behaviour has a test that fails without the change
(`scripts/agent/test_architecture_contracts.py`)
- [ ] Protected-path or contract change named above, with the reason it
is required

No protected path is in the diff. The contract lives under
`architecture/` so `docs/schemas/**` stays untouched.

## Internal logic (touched behavior-bearing code)

- [x] Guard clauses handle invalid, stale, cancelled, absent,
unauthorized, and terminal cases before the happy path
- [x] Untrusted input is parsed once at the boundary into trusted typed
or domain state, with no repeated checks downstream
- [x] Invalid state stops before partial mutation or publication and
returns a descriptive error or result
- [x] Names carry the domain intent, and comments explain rationale
rather than restating the code

The checker parses architecture YAML with a block subset (CI does not
install PyYAML), then fails closed on missing lanes, missing budgets,
unknown `budget:` refs, sealed fixture edits without
`sealed_output_approval: human`, and loosened caps without an unresolved
marker.

## Anti-slop pass

- [x] Redundant or explanatory comments that do not match the file's
style removed
- [x] Abnormal defensive checks and broad try/catch blocks removed where
a trusted upstream boundary already guarantees the invariant, with real
boundary and safety checks kept
- [x] No `any` or equivalent cast added only to suppress a type error
- [x] Python imports stay at file scope unless a local import is
required
- [x] Generated boilerplate, needless wrappers, and local-style drift
removed
- [x] Validation, security, cancellation, provenance, and failure
handling preserved

Anti-slop summary (1-3 sentences):

The YAML loader stays because the agent environment must not add a
dependency. Declared budgets keep an explicit `todo` instead of a fake
measurement. Adapter text is generated from `agent-policy.json`.

## Security and rollback

- [x] Untrusted input validated at the trust boundary; no new unsafe
construct without an inline justification
- [x] Rollback: revert `1bf13aa3d39c7d6c9d80e32e14fb762829049fe1`. No
persistence format or public ABI changes.

## Docs

- [x] Docs updated in this PR (`docs/architecture-source-of-truth.md`,
generated policy adapters). The design narrative is in the worker store
at `/cursor/stores/self/docs/architecture-contracts.md`.

## Self-review (BSP-002 §4.3)

- [ ] Reviewed in the diff view, not the editor, at least 30 minutes
after the final commit; overnight if the change touches
security-sensitive code, data handling, or public API surface

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-a5ef665f-cbf8-598c-bf36-f801e453806f?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-a5ef665f-cbf8-598c-bf36-f801e453806f&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/studio-berry/codesmith/loop/pr/672?ref=codesmith_pr_footer"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1792644469&installation_model_id=435800&pr_number=672&ref=codesmith_pr_footer&repository=studio-berry%2Floop&return_to=https%3A%2F%2Fgithub.com%2Fstudio-berry%2Floop%2Fpull%2F672&signature=1a42bc21510bb2abfc042d0546bfa2f095575711fb705bbf7ccb849f46b2ae1c"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
@mberrys mberrys added this to the 0.3.0 milestone Sep 22, 2026
@mberrys mberrys changed the title Dev 0.3.0 qualification: dev → unstable Sep 22, 2026
@mberrys
mberrys changed the base branch from stable to unstable September 22, 2026 18:11
@mberrys
mberrys marked this pull request as draft September 22, 2026 18:11
Catalog drift from #673 left the image-decode guard test unclaimed;
architecture contracts and Release Gate failed until it is listed in
agent-policy core paths/tests.

Co-authored-by: michael berry <mberrys@users.noreply.github.com>
@mberrys
mberrys marked this pull request as ready for review September 22, 2026 21:46
@mberrys
mberrys merged commit 46ef707 into unstable Sep 22, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants