Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
59b4bb5
fix(security): update transitive nanoid to 3.3.17
seonghobae Aug 8, 2026
0b28347
docs(security): record nanoid remediation evidence
seonghobae Aug 8, 2026
f0b0252
fix(security): minimize nanoid lockfile remediation
seonghobae Aug 8, 2026
0153b55
docs(changelog): record nanoid security remediation
seonghobae Aug 8, 2026
5123462
docs(security): correct nanoid remediation evidence
seonghobae Aug 8, 2026
1a7a6ee
test(security): require atomic publisher ref lease
seonghobae Aug 8, 2026
9b81b92
docs(security): define atomic write and toolchain evidence
seonghobae Aug 8, 2026
76c8f26
docs(security): define atomic publisher ref lease
seonghobae Aug 8, 2026
f60c4d9
fix(security): lease product publisher ref updates
seonghobae Aug 8, 2026
16941a9
test(security): bind mutation-order check to leased push
seonghobae Aug 8, 2026
ab0819a
docs(security): record publisher lease verification boundary
seonghobae Aug 8, 2026
9af9d78
test(ci): require exact pull-request head checkout
seonghobae Aug 8, 2026
9e2606d
fix(ci): verify exact pull-request head
seonghobae Aug 8, 2026
b6f838c
docs(security): bind nanoid evidence to exact-head CI
seonghobae Aug 8, 2026
21dba32
test(ci): require reviewer exact-head binding
seonghobae Aug 8, 2026
aab81f7
fix(ci): bind reviewer checks to exact PR head
seonghobae Aug 8, 2026
e66f389
docs(security): classify exact-head scan evidence
seonghobae Aug 8, 2026
6160e7d
test(ci): pin exact Node and npm execution contract
seonghobae Aug 8, 2026
739753f
fix(ci): pin Node npm and install semantics
seonghobae Aug 8, 2026
a0b7c48
docs(security): pin nanoid reproduction toolchain
seonghobae Aug 8, 2026
e0106ce
test(ci): accept exact Node 24 patch versions
seonghobae Aug 8, 2026
7f210d0
test(security): bind publisher PR identity after creation
seonghobae Aug 8, 2026
048bffe
fix(security): bind created PR identity before publisher cleanup
seonghobae Aug 8, 2026
a284370
docs(security): document post-create PR identity binding
seonghobae Aug 8, 2026
d144f01
Merge a284370cdc3afd2a5b6306c9b5933f6bd1373627 into e0106ce16b7b8b493…
seonghobae Aug 8, 2026
97b9a2f
test(scheduler): require realistic remediation evidence
seonghobae Aug 8, 2026
a0d4ffe
docs(scheduler): require realistic remediation before escalation
seonghobae Aug 8, 2026
8649f6f
fix(scheduler): preserve remediation contract tokens
seonghobae Aug 8, 2026
44980ec
docs(scheduler): record realistic remediation decision
seonghobae Aug 8, 2026
e86c3be
docs(changelog): record realistic scheduler remediation
seonghobae Aug 8, 2026
d35062e
test(security): require post-create PR queue revalidation
seonghobae Aug 8, 2026
a2da363
test(security): require machine-readable PR publication identity
seonghobae Aug 8, 2026
1b2e778
fix(security): bind product PR creation to machine identity and queue
seonghobae Aug 8, 2026
9f4fb7f
test(security): arm PR cleanup before creation response
seonghobae Aug 8, 2026
45faa95
test(security): bind trap assertions to acceptance path
seonghobae Aug 8, 2026
f60f4bb
test(workflow): bind PR creation assertions to REST publisher
seonghobae Aug 8, 2026
de07390
docs(security): record recoverable REST PR publication
seonghobae Aug 8, 2026
1375711
docs(changelog): record recoverable PR publication
seonghobae Aug 8, 2026
8ae32bb
test(automation): require RCA and feasibility-gated scheduler action
seonghobae Aug 8, 2026
2a120cb
test(automation): bind RCA protocol to scheduler-consumed guidance
seonghobae Aug 8, 2026
13ea7ee
fix(automation): require feasible RCA before scheduler action
seonghobae Aug 8, 2026
deb53c9
docs(automation): record RCA feasibility scheduler contract
seonghobae Aug 8, 2026
abd973a
test(governance): expose stacked security scan trigger gap
seonghobae Aug 8, 2026
9103c46
fix(governance): model stacked security scan trigger reality
seonghobae Aug 8, 2026
a884e13
docs(governance): record stacked Security Scan trigger RCA
seonghobae Aug 8, 2026
7c4d156
docs(changelog): record stacked security scan trigger boundary
seonghobae Aug 8, 2026
9600fcb
test(docs): require authoritative architecture spine
seonghobae Aug 9, 2026
73bdcb6
chore(docs): keep architecture spine with owning PR
seonghobae Aug 9, 2026
6e73aa7
test(automation): require deliverable handoff before scheduler exit
seonghobae Aug 9, 2026
9a19f21
feat(automation): require deliverable handoff and double exit sweep
seonghobae Aug 9, 2026
01f25c5
test(automation): require changelog for continuation contract
seonghobae Aug 9, 2026
a937c59
docs(changelog): record work-conserving deliverable handoff
seonghobae Aug 9, 2026
5245682
test(security): require model shell secret compartment
seonghobae Aug 9, 2026
b61f9a1
fix(security): deny model shell in NIM credential compartment
seonghobae Aug 9, 2026
8591909
test(security): execute OpenCode secret compartment hook
seonghobae Aug 9, 2026
0c7cd30
docs(security): record OpenCode provider secret compartment
seonghobae Aug 9, 2026
235b2c0
test(docs): reject stale proposer credential and scan guidance
seonghobae Aug 9, 2026
2095359
docs(governance): align proposer credential and scan guidance
seonghobae Aug 9, 2026
b293a4a
test(governance): align remediation credential boundary
seonghobae Aug 9, 2026
3171b36
test(governance): reject fabricated proposer verification evidence
seonghobae Aug 9, 2026
be2dd44
fix(automation): align proposer prompt with credential boundary
seonghobae Aug 9, 2026
62b74b6
test(automation): normalize prompt contract whitespace
seonghobae Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,38 @@ jobs:
contents: read
steps:
- name: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: verify exact checkout
shell: bash
env:
NOEMA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
set -euo pipefail
if [[ ! "$NOEMA_EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Invalid expected head SHA.\n'
exit 1
fi
test "$(git rev-parse HEAD)" = "$NOEMA_EXPECTED_HEAD_SHA"

- name: setup node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24"
node-version: "24.19.0"
cache: npm

- name: verify package-manager toolchain
shell: bash
run: |
set -euo pipefail
test "$(node --version)" = "v24.19.0"
test "$(npm --version)" = "11.17.0"

- name: install
run: npm ci
run: npm ci --legacy-peer-deps=false --install-links=false

- name: release verify
run: npm run release:verify
186 changes: 147 additions & 39 deletions .github/workflows/hourly-product-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,19 @@ jobs:
contextual-orchestrator for every new product-runtime LLM path. Do not alter
the existing reviewer-agent credential names, trust boundary, or provider route.

Work test-first: add one realistic executable regression or product contract,
run it, and record the expected failure before implementation. Then make the
smallest coherent change that closes the selected gap. Tests must exercise
realistic Noema traffic, operational, security, provenance, or buyer workflows,
including adversarial and failure cases where applicable. Maintain 100% production
statement and branch coverage plus 100% production function coverage. When reviewer
Python changes, maintain 100% line and branch coverage and 100% docstring coverage.
Public modules, classes, functions, methods, properties, inputs, outputs, errors,
and trust boundaries must be understandable to a beginner without reverse-engineering.
The credential-bearing proposer has no shell execution authority. Do not claim that
you executed tests or shell commands. Work test-first at the proposal boundary: add
one realistic executable regression or product contract and describe the expected
pre-implementation RED condition before implementation. Then make the smallest
coherent source change that closes the selected gap. A separate uncredentialed verifier
will execute the proposal on a fresh runner. Treat execution results pending trusted
verifier evidence until that verifier succeeds. Tests must ultimately exercise realistic
Noema traffic, operational, security, provenance, or buyer workflows, including adversarial
and failure cases where applicable. Maintain 100% production statement and branch coverage
plus 100% production function coverage. When reviewer Python changes, maintain 100% line
and branch coverage and 100% docstring coverage. Public modules, classes, functions,
methods, properties, inputs, outputs, errors, and trust boundaries must be understandable
to a beginner without reverse-engineering.

Where behavior is ambiguous, use current authoritative international standards,
primary official documentation, or peer-reviewed research. Record sources and
Expand All @@ -168,8 +172,9 @@ jobs:
Preserve standalone use and modular MSA integration. If a database boundary is
introduced, every new object must use a descriptive two-word-or-longer snake_case
name. Do not add skipped, ignored, quarantined, or weakened release-gate tests.
Run focused tests and npm run release:verify. Update CHANGELOG.md and all affected
product, architecture, security, operations, API, support, and buyer documentation.
Describe the focused and complete verification that the separate uncredentialed verifier
must execute. Update CHANGELOG.md and all affected product, architecture, security,
operations, API, support, and buyer documentation.

Follow Semantic Versioning. Change the package version only when the integrated
repository is genuinely release-ready and all immutable release, deployment,
Expand All @@ -184,9 +189,9 @@ jobs:

Leave the working tree with one bounded increment and write PR_MESSAGE.md at the
repository root. Put the pull-request title on the first line, then a body containing
the product gap, design, RED-to-GREEN evidence, complete verification commands and
results, APA 7 sources where applicable, version decision, and explicit residual risk.
A separate uncredentialed verifier will execute the proposal on a fresh runner.
the product gap, design, expected RED condition, implementation rationale, requested
verifier commands, APA 7 sources where applicable, version decision, and explicit residual
risk. A separate uncredentialed verifier will execute the proposal on a fresh runner.
A third credential-bearing publisher will reconstruct the same immutable artifact
without executing proposed code. Existing review → repair → exact-head Checks → merge
governance retains every final decision.
Expand Down Expand Up @@ -849,45 +854,148 @@ jobs:
GH_TOKEN: ${{ steps.maintainer_app.outputs.token }}
run: |
set -euo pipefail
umask 077
title="$(cat "$RUNNER_TEMP/pr-title.txt")"
body_file="$RUNNER_TEMP/pr-body.md"
branch="nim-agent/product-dev-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
expected_base="${{ needs.propose_product_increment.outputs.base_sha }}"
repo_owner="${GITHUB_REPOSITORY%%/*}"

if ! [[ "$expected_base" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::proposal_base_invalid"
exit 1
fi

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$branch"
git -c core.hooksPath=/dev/null commit -m "$title"
gh auth setup-git

set +e
git ls-remote --exit-code --heads origin "refs/heads/${branch}" >/dev/null 2>&1
remote_status=$?
set -e
case "$remote_status" in
0)
echo "::error::proposal_branch_already_exists"
exit 1
;;
2) ;;
*)
echo "::error::proposal_branch_inventory_unavailable"
exit 1
;;
esac
proposal_head="$(git rev-parse HEAD)"
if ! [[ "$proposal_head" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::proposal_head_invalid"
exit 1
fi
if ! git push --force-with-lease="refs/heads/${branch}:" origin "HEAD:refs/heads/${branch}"; then
echo "::error::proposal_branch_create_lease_rejected"
exit 1
fi

cleanup_remote_branch() {
git push origin --delete "$branch" >/dev/null 2>&1 || true
git push --force-with-lease="refs/heads/${branch}:${proposal_head}" origin ":refs/heads/${branch}" >/dev/null 2>&1 || true
}
trap cleanup_remote_branch ERR
git push origin "HEAD:refs/heads/${branch}"
pr_url="$(
gh pr create \
--repo "$GITHUB_REPOSITORY" \
--base "$DEFAULT_BRANCH" \
--head "$branch" \
--title "$title" \
--body-file "$body_file"
)"

marker_nonce="$(od -An -N32 -tx1 /dev/urandom | tr -d '[:space:]')"
if ! [[ "$marker_nonce" =~ ^[0-9a-f]{64}$ ]]; then
echo "::error::publication_marker_generation_failed"
false
fi
publication_marker="noema-publication-${marker_nonce}"
pr_request_file="$RUNNER_TEMP/pr-create.json"
jq -n \
--arg title "$title" \
--arg head "$branch" \
--arg base "$DEFAULT_BRANCH" \
--rawfile body "$body_file" \
--arg marker "$publication_marker" \
'{title: $title, head: $head, base: $base, body: ($body + "\n\n<!-- " + $marker + " -->")}' \
>"$pr_request_file"
chmod 0600 "$pr_request_file"

pr_number=""
recover_created_pr_number() {
local candidates candidate candidate_json candidate_head candidate_base candidate_body recovered=""
if ! candidates="$(
gh api --paginate \
"repos/${GITHUB_REPOSITORY}/pulls?state=open&head=${repo_owner}:${branch}&per_page=100" \
--jq '.[].number'
)"; then
return 1
fi
while IFS= read -r candidate; do
[ -n "$candidate" ] || continue
if ! [[ "$candidate" =~ ^[1-9][0-9]*$ ]]; then
return 1
fi
if ! candidate_json="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${candidate}")"; then
return 1
fi
candidate_head="$(jq -r '.head.sha // empty' <<<"$candidate_json")"
candidate_base="$(jq -r '.base.sha // empty' <<<"$candidate_json")"
candidate_body="$(jq -r '.body // empty' <<<"$candidate_json")"
if [ "$candidate_head" = "$proposal_head" ] \
&& [ "$candidate_base" = "$expected_base" ] \
&& grep -Fq -- "$publication_marker" <<<"$candidate_body"; then
if [ -n "$recovered" ]; then
return 1
fi
recovered="$candidate"
fi
done <<<"$candidates"
[ -n "$recovered" ] || return 1
printf '%s\n' "$recovered"
}

cleanup_created_pr() {
trap - ERR
if ! [[ "${pr_number:-}" =~ ^[1-9][0-9]*$ ]]; then
pr_number="$(recover_created_pr_number 2>/dev/null || true)"
fi
if [[ "${pr_number:-}" =~ ^[1-9][0-9]*$ ]]; then
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/pulls/${pr_number}" -f state=closed >/dev/null 2>&1 || true
fi
cleanup_remote_branch
}
trap cleanup_created_pr ERR

if ! created_pr_json="$(
gh api --method POST "repos/${GITHUB_REPOSITORY}/pulls" --input "$pr_request_file"
)"; then
echo "::error::created_pull_request_request_failed"
false
fi
pr_number="$(jq -r '.number // empty' <<<"$created_pr_json")"
if ! [[ "$pr_number" =~ ^[1-9][0-9]*$ ]]; then
echo "::error::created_pull_request_number_invalid"
false
fi
pr_url="https://github.com/${GITHUB_REPOSITORY}/pull/${pr_number}"

if ! created_pr_json="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${pr_number}")"; then
echo "::error::created_pull_request_identity_unavailable"
false
fi
live_pr_head="$(jq -r '.head.sha // empty' <<<"$created_pr_json")"
live_pr_base="$(jq -r '.base.sha // empty' <<<"$created_pr_json")"
if ! [[ "$live_pr_head" =~ ^[0-9a-f]{40}$ ]] \
|| ! [[ "$live_pr_base" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::created_pull_request_identity_invalid"
false
fi
if [ "$live_pr_head" != "$proposal_head" ]; then
echo "::error::created_pull_request_head_mismatch"
false
fi
if [ "$live_pr_base" != "$expected_base" ]; then
echo "::error::created_pull_request_base_mismatch"
false
fi

if ! open_pr_numbers="$(
gh api --paginate \
"repos/${GITHUB_REPOSITORY}/pulls?state=open&per_page=100" \
--jq '.[].number'
)"; then
echo "::error::created_pull_request_queue_inventory_unavailable"
false
fi
if [ "$open_pr_numbers" != "$pr_number" ]; then
echo "::error::created_pull_request_queue_conflict"
false
fi

trap - ERR
{
echo "Opened bounded pull request: $pr_url"
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/reviewer-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,21 @@ jobs:
- name: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: verify exact checkout
shell: bash
env:
NOEMA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
set -euo pipefail
if [[ ! "$NOEMA_EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Invalid expected head SHA.\n'
exit 1
fi
test "$(git rev-parse HEAD)" = "$NOEMA_EXPECTED_HEAD_SHA"

- name: setup python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
Expand Down
18 changes: 18 additions & 0 deletions .opencode/plugins/noema-secret-compartment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Keeps the NVIDIA NIM provider credential inside the trusted OpenCode process.
*
* OpenCode's bash tool executes model-selected child processes. The hourly
* product-development agent needs the NVIDIA provider credential in its own
* process to call the model, so model-selected shell execution is denied at
* the plugin boundary. Deterministic source and test execution remains the
* responsibility of the separate uncredentialed verifier job.
*/
export const NoemaSecretCompartment = async () => ({
"tool.execute.before": async (input) => {
if (input.tool === "bash") {
throw new Error(
"NVIDIA NIM credential compartment: model shell execution is disabled; write the proposal and let the separate uncredentialed verifier execute tests.",
);
}
},
});
Loading
Loading