-
Notifications
You must be signed in to change notification settings - Fork 0
feat(operations): run default-branch Maintainer App preflight #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
e91c0ea
feat(operations): add default-branch Maintainer App preflight
seonghobae 0f7fe63
test(operations): retain dry-run execution failures
seonghobae f1a7d7f
fix(operations): retain dry-run execution failures
seonghobae 63b9c3a
test(operations): require normalized dry-run evidence
seonghobae 6a35034
fix(operations): normalize bounded dry-run evidence
seonghobae 53c9fe6
docs(operations): document dry-run evidence normalization
seonghobae a218911
test(operations): define bounded evidence normalization
seonghobae feac2f1
feat(operations): normalize bounded dry-run evidence
seonghobae 5265b51
refactor(test): verify reusable evidence normalizer
seonghobae 98dd215
refactor(operations): reuse tested evidence normalizer
seonghobae e96333f
fix(operations): atomically replace unsafe evidence
seonghobae 5e38505
test(operations): reject noncanonical and symlinked evidence writes
seonghobae 0896594
fix(operations): reject ambiguous timestamps and temp symlinks
seonghobae c522e04
docs(operations): document normalized readiness evidence
seonghobae b57c38e
test(ci): require 100 percent production coverage execution
seonghobae 25e5c4f
docs(changelog): record normalized preflight evidence
seonghobae df1e375
ci(coverage): execute 100 percent production coverage gate
seonghobae b6ab625
ci(coverage): include evidence normalizer in production gate
seonghobae 6a98fed
test(ci): require exact-PR workflow concurrency
seonghobae 29f9ee4
test(ci): require immutable pull-request workflow sources
seonghobae 10d10a7
ci(workflows): cancel stale exact-PR verification runs
seonghobae 52214a1
ci(workflows): cancel stale exact-PR reviewer runs
seonghobae 301cc7e
test(operations): cover the complete evidence trust boundary
seonghobae e6bb86d
fix(operations): close evidence descriptor race and coverage gaps
seonghobae 382310d
docs(operations): document exact evidence and CI trust boundaries
seonghobae a3b936a
test(operations): require runner-isolated preflight evidence
seonghobae 3ebccfc
fix(operations): isolate preflight evidence from checkout
seonghobae 6b789b3
fix(test): escape runner context literals
seonghobae 14e60ee
docs(operations): document runner-isolated evidence paths
seonghobae fb69035
test(operations): reject duplicate JSON evidence keys
seonghobae 609ddc0
fix(operations): reject ambiguous duplicate JSON keys
seonghobae 1ef7d76
docs(operations): document duplicate-key evidence rejection
seonghobae 5351cb8
docs(changelog): record bounded JSON ambiguity rejection
seonghobae fdd5bec
test(security): reject malformed UTF-8 evidence
seonghobae 8d5f418
test(operations): reject malformed UTF-8 evidence
seonghobae 0488187
fix(security): fail closed on malformed UTF-8 evidence
seonghobae 1e117cd
docs(doctoring): record fatal UTF-8 evidence boundary
seonghobae 8ad7410
test(security): consolidate UTF-8 evidence regression
seonghobae 157d51c
test(security): preserve allowlisted international evidence
seonghobae 622e7ab
test(performance): forbid quadratic primitive suffix copies
seonghobae 13adf12
test(operations): require stable failure-report identity
seonghobae f590c03
fix(performance): scan JSON primitives without suffix copies
seonghobae 39fd987
fix(workflows): preserve bounded failure reason evidence
seonghobae 6decdc9
test(coverage): exercise URL parser fail-closed branches
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| name: maintainer-app-readiness | ||
| run-name: Noema Maintainer App pre-activation audit | ||
|
|
||
| on: | ||
| # repository_dispatch is evaluated only from the default branch. A caller | ||
| # cannot select unreviewed workflow code for these privileged token mints. | ||
| repository_dispatch: | ||
| types: [maintainer-app-readiness] | ||
|
|
||
| concurrency: | ||
| group: noema-maintainer-app-readiness | ||
| cancel-in-progress: true | ||
|
|
||
| # GITHUB_TOKEN is used only by the trusted checkout. Audit API calls use the | ||
| # repository-scoped Maintainer token; the Reviewer token is never exposed to a | ||
| # script and supplies only authenticated App slug/installation identity output. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| preflight: | ||
| name: maintainer-app-pre-activation-audit | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| env: | ||
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | ||
| steps: | ||
| - name: checkout event-bound default-branch commit | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| with: | ||
| # For repository_dispatch, github.sha is the default-branch commit | ||
| # bound when the event was created. Do not re-resolve a moving branch. | ||
| ref: ${{ github.sha }} | ||
| persist-credentials: false | ||
|
|
||
| - name: mint repository-scoped Maintainer App token | ||
| id: maintainer_app | ||
| continue-on-error: true | ||
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| with: | ||
| client-id: ${{ vars.NOEMA_MAINTAINER_APP_CLIENT_ID }} | ||
| private-key: ${{ secrets.NOEMA_MAINTAINER_APP_PRIVATE_KEY }} | ||
| owner: ContextualWisdomLab | ||
| repositories: noema | ||
| permission-actions: read | ||
| permission-checks: read | ||
| permission-contents: write | ||
| permission-metadata: read | ||
| permission-pull-requests: write | ||
| permission-statuses: read | ||
|
|
||
| - name: mint repository-scoped Reviewer App identity token | ||
| id: reviewer_app | ||
| continue-on-error: true | ||
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| with: | ||
| client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} | ||
| private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} | ||
| owner: ContextualWisdomLab | ||
| repositories: noema | ||
| permission-metadata: read | ||
|
|
||
| - name: setup Node.js | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | ||
| with: | ||
| node-version: "24" | ||
|
|
||
| - name: audit active main governance | ||
| id: governance | ||
| continue-on-error: true | ||
| env: | ||
| GH_TOKEN: ${{ steps.maintainer_app.outputs.token }} | ||
| NOEMA_GOVERNANCE_AUDIT_PATH: ${{ runner.temp }}/noema-maintainer-app-readiness/main-governance-audit.json | ||
| run: node scripts/main-governance-audit.mjs | ||
|
|
||
| - name: audit effective Maintainer App identity and access | ||
| id: readiness | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| GH_TOKEN: ${{ steps.maintainer_app.outputs.token }} | ||
| NOEMA_MAINTAINER_APP_SLUG: ${{ steps.maintainer_app.outputs.app-slug }} | ||
| NOEMA_MAINTAINER_INSTALLATION_ID: ${{ steps.maintainer_app.outputs.installation-id }} | ||
| NOEMA_REVIEWER_APP_SLUG: ${{ steps.reviewer_app.outputs.app-slug }} | ||
| NOEMA_REVIEWER_INSTALLATION_ID: ${{ steps.reviewer_app.outputs.installation-id }} | ||
| NOEMA_REVIEWER_LOGIN: ${{ vars.NOEMA_REVIEWER_LOGIN }} | ||
| NOEMA_MAINTENANCE_ENABLED: ${{ vars.NOEMA_MAINTENANCE_ENABLED }} | ||
| NOEMA_GOVERNANCE_AUDIT_PATH: ${{ runner.temp }}/noema-maintainer-app-readiness/main-governance-audit.json | ||
| NOEMA_MAINTAINER_READINESS_PATH: ${{ runner.temp }}/noema-maintainer-app-readiness/maintainer-app-readiness.json | ||
| run: node scripts/maintainer-app-readiness.mjs | ||
|
|
||
| - name: inspect commercial-readiness loop without writes | ||
| id: dry_run | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| GH_TOKEN: ${{ steps.maintainer_app.outputs.token }} | ||
| NOEMA_REVIEWER_LOGIN: ${{ vars.NOEMA_REVIEWER_LOGIN }} | ||
| MAINTAINER_APP_OUTCOME: ${{ steps.maintainer_app.outcome }} | ||
| run: | | ||
| set -euo pipefail | ||
| report_path="$RUNNER_TEMP/noema-maintainer-app-readiness/commercial-readiness-loop-dry-run.json" | ||
| write_failure_report() { | ||
| local reasonCode="$1" | ||
| local reasonDetail="$2" | ||
| FAILURE_CODE="$reasonCode" FAILURE_DETAIL="$reasonDetail" REPORT_PATH="$report_path" \ | ||
| node --input-type=module <<'NODE' | ||
| import { mkdirSync, writeFileSync } from "node:fs"; | ||
| import { dirname } from "node:path"; | ||
|
|
||
| const reportPath = process.env.REPORT_PATH; | ||
| const reasonCode = process.env.FAILURE_CODE; | ||
| const reasonDetail = process.env.FAILURE_DETAIL; | ||
| const report = { | ||
| schemaVersion: 1, | ||
| repository: "ContextualWisdomLab/noema", | ||
| generatedAt: new Date().toISOString(), | ||
| apply: false, | ||
| openPullRequestCount: null, | ||
| remainingOpenPullRequestCount: null, | ||
| results: [ | ||
| { | ||
| number: null, | ||
| result: "operational_error", | ||
| reasons: [{ code: reasonCode, detail: reasonDetail }], | ||
| }, | ||
| ], | ||
| }; | ||
| mkdirSync(dirname(reportPath), { recursive: true }); | ||
| writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8"); | ||
| NODE | ||
| } | ||
|
|
||
| if [ "$MAINTAINER_APP_OUTCOME" != "success" ]; then | ||
| write_failure_report \ | ||
| "maintainer_token_unavailable" \ | ||
| "Maintainer App token mint did not succeed; no GitHub API requests were attempted." | ||
| exit 1 | ||
| fi | ||
|
|
||
| set +e | ||
| node scripts/hourly-commercial-readiness.mjs --report "$report_path" | ||
| loop_status=$? | ||
| set -e | ||
| if [ "$loop_status" -ne 0 ] && [ ! -s "$report_path" ]; then | ||
| write_failure_report \ | ||
| "commercial_loop_failed" \ | ||
| "Commercial-readiness dry run failed before it could retain its bounded report." | ||
| fi | ||
| exit "$loop_status" | ||
|
|
||
| - name: normalize bounded commercial-loop evidence | ||
| id: dry_run_evidence | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| REPORT_PATH: ${{ runner.temp }}/noema-maintainer-app-readiness/commercial-readiness-loop-dry-run.json | ||
| run: node scripts/normalize-commercial-readiness-evidence.mjs | ||
|
|
||
| - name: upload main governance evidence | ||
| if: always() | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: main-governance-audit | ||
| path: ${{ runner.temp }}/noema-maintainer-app-readiness/main-governance-audit.json | ||
| if-no-files-found: error | ||
| retention-days: 90 | ||
|
|
||
| - name: upload Maintainer App readiness evidence | ||
| if: always() | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: maintainer-app-readiness | ||
| path: ${{ runner.temp }}/noema-maintainer-app-readiness/maintainer-app-readiness.json | ||
| if-no-files-found: error | ||
| retention-days: 90 | ||
|
|
||
| - name: upload no-write commercial loop evidence | ||
| if: always() | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: commercial-readiness-loop-dry-run | ||
| path: ${{ runner.temp }}/noema-maintainer-app-readiness/commercial-readiness-loop-dry-run.json | ||
| if-no-files-found: error | ||
| retention-days: 90 | ||
|
|
||
| - name: enforce pre-activation gates | ||
| if: always() | ||
| env: | ||
| MAINTAINER_APP_OUTCOME: ${{ steps.maintainer_app.outcome }} | ||
| REVIEWER_APP_OUTCOME: ${{ steps.reviewer_app.outcome }} | ||
| GOVERNANCE_OUTCOME: ${{ steps.governance.outcome }} | ||
| READINESS_OUTCOME: ${{ steps.readiness.outcome }} | ||
| DRY_RUN_OUTCOME: ${{ steps.dry_run.outcome }} | ||
| DRY_RUN_EVIDENCE_OUTCOME: ${{ steps.dry_run_evidence.outcome }} | ||
| run: | | ||
| set -euo pipefail | ||
| failed=0 | ||
| for gate in MAINTAINER_APP_OUTCOME REVIEWER_APP_OUTCOME GOVERNANCE_OUTCOME READINESS_OUTCOME DRY_RUN_OUTCOME DRY_RUN_EVIDENCE_OUTCOME; do | ||
| value="${!gate}" | ||
| if [ "$value" != "success" ]; then | ||
| printf '::error::%s was %s, not success.\n' "$gate" "$value" | ||
| failed=1 | ||
| fi | ||
| done | ||
| exit "$failed" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
docs/doctoring/commercial-readiness-evidence-utf8-boundary.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Commercial-readiness evidence UTF-8 boundary | ||
|
|
||
| ## Decision | ||
|
|
||
| Noema treats the dry-run commercial-readiness artifact as an exact byte-level protocol input. The normalizer therefore decodes the bounded `Buffer` with a single reusable `TextDecoder("utf-8", { fatal: true })` before duplicate-key scanning or `JSON.parse`. A malformed UTF-8 sequence throws and is converted into the fixed `dry_run_report_invalid` report; replacement-character decoding is not accepted. | ||
|
|
||
| This preserves the distinction between the bytes produced by the no-write maintenance run and the canonical evidence retained for due diligence. Without fatal decoding, JavaScript replacement semantics could convert malformed wire bytes to U+FFFD, after which an invalid byte sequence located in an allowlist-dropped field could be silently accepted as valid evidence. | ||
|
|
||
| ## Standards rationale | ||
|
|
||
| RFC 8259 requires JSON exchanged between systems outside a closed ecosystem to use UTF-8. The WHATWG Encoding Living Standard defines a fatal decoder mode that returns an error instead of inserting U+FFFD, and its `TextDecoder` API throws a `TypeError` when fatal decoding encounters an error. Node.js 24 exposes this WHATWG-compatible API globally and documents the same failure behavior. | ||
|
|
||
| The implementation does not claim formal conformance certification. It applies the interoperable UTF-8 and fail-closed decoding requirements that are relevant to this evidence boundary. | ||
|
|
||
| ## Verification contract | ||
|
|
||
| The regression suite supplies malformed UTF-8 inside a syntactically valid, unknown JSON member. The expected result is fixed invalid evidence, proving that allowlist dropping cannot hide malformed source bytes. A companion case supplies valid Korean and punctuation text in an allowlisted reason detail and requires exact preservation, proving that the gate rejects malformed encoding rather than non-ASCII content. | ||
|
|
||
| ## APA 7th references | ||
|
|
||
| Bray, T. (2017). *The JavaScript Object Notation (JSON) data interchange format* (RFC 8259). Internet Engineering Task Force. https://doi.org/10.17487/RFC8259 | ||
|
|
||
| Node.js contributors. (2026). *Util: Class TextDecoder* (Node.js v24.11.0 documentation). Retrieved August 4, 2026, from https://nodejs.org/download/release/v24.11.0/docs/api/util.html | ||
|
|
||
| WHATWG. (2026, May 21). *Encoding* (Living Standard). Retrieved August 4, 2026, from https://encoding.spec.whatwg.org/ | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.