Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 11 additions & 2 deletions .github/skills/add-operational-value/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,12 @@ Use `/add-operational-value OWNER/REPO` to list repository workflows or `/add-op
and validation. The automatic registration in step 6 records only the selected
pair; function files alone are not runtime authority. Have the policy
and collector reviewed, committed and deployed together. Do not widen Activity
scope or enable the measured workflow. Existing collection cycles invoke
registered collectors at their frozen cadence; a failed refresh must retain
scope or enable the measured workflow. Recurring remote acquisition remains
deferred until enforceable shared API budgets protect ordinary Activity.
Registration is visible as enabled-but-deferred, not a promise of fresh
measurements. Bounded local-only computations run independently on eligible
published snapshots; they do not bypass fast, public-only or API-limit gates.
An admitted collector must honor the frozen cadence; a failed refresh must retain
the prior accepted evidence and timestamps. `value disable` revokes future
collection without removing history and persists an explicit disable that
automatic registration must respect. Re-enabling requires explicit
Expand Down Expand Up @@ -465,3 +469,8 @@ proof, not a target for fresh collections.
- It may emit provisional pre-maturity values, but must include their interim maturity status and workflow adoption timestamp so consumers can mark them dubious and place adoption in temporal plots.
- It emits no record for unsupported repositories and fails closed on malformed requests, inaccessible evidence, or invalid collection results.
- Historical evaluation and CAO collection must import the same shared module so evidence and scoring cannot drift.
- A module may export `localCollection = true` only when `collectBatch` can use
the supplied canonical database or local immutable evidence without remote
calls or download fallbacks. Automatic local-only execution admits the reviewed
standard adapter by source digest; keep its template and package copies equal
and update that reviewed digest with adapter changes. Other adapters defer.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ try {
}
if (
request?.schemaVersion !== 1
|| (request.localOnly !== undefined && typeof request.localOnly !== "boolean")
|| typeof request.timestamp !== "string"
|| Number.isNaN(Date.parse(request.timestamp))
|| !Array.isArray(request.repositories)
Expand Down Expand Up @@ -93,11 +94,13 @@ for (const moduleFile of moduleFiles) {
valueIds: definition.metrics.map((metric) => `${definition.slug}.${metric.id}`),
...(valueModule.historyBatch === true ? { historyBatch: true } : {}),
}));
if (request.localOnly && request.disabledWorkflows?.includes(definition.slug)) continue;

const supported = new Set(
definition.evidence.repositories.map((repository) => String(repository).toLowerCase()),
);
const repositories = request.repositories.filter((repository) => supported.has(repository.toLowerCase()));
const repositories = request.repositories.filter((repository) => supported.has(repository.toLowerCase())
&& !(request.localOnly && request.disabledCollections?.includes(`${repository.toLowerCase()}:${definition.slug}`)));
if (repositories.length === 0) continue;

const { durationDays, maturationDays } = definition.evidence.window;
Expand Down Expand Up @@ -176,10 +179,16 @@ for (const moduleFile of moduleFiles) {
}
}

if (request.localOnly && valueModule.localCollection !== true) {
console.log(JSON.stringify({ kind: "operational_value_deferred", workflowSlug: definition.slug,
reason: "Remote acquisition requires enforceable shared API admission; retained evidence is unchanged." }));
continue;
}
let collections;
try {
collections = await collectBatch(windows, {
database: request.database,
...(request.localOnly ? { localOnly: true } : {}),
...(history ? { history: true } : {}),
});
} catch (error) {
Expand Down
88 changes: 55 additions & 33 deletions .github/workflows/cao-activity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ on:
paths:
- .github/workflows/cao.json
- .github/workflows/cao-activity.yml
- .github/workflows/cao-value-local.yml
- .github/workflows/cao-dashboard.yml
- ".github/value-collectors/**"
- ".github/skills/add-operational-value/**"
Expand Down Expand Up @@ -350,15 +351,18 @@ jobs:
--graphql-cost-budget 25 \
--graphql-min-remaining 500

- name: Compute owner-scoped operational value
- name: Retain Value evidence and publish collection health
id: value-health
timeout-minutes: 1
continue-on-error: true
env:
REPORT_PUBLIC_ONLY: ${{ matrix.publicOnly == true }}
ACTIVITY_ARTIFACT: ${{ matrix.artifact }}
ACTIVITY_DATABASE: ${{ runner.temp }}/cao-activity/collection.sqlite
ACTIVITY_OWNER: ${{ matrix.owner }}
CAO_OPERATIONAL_VALUE_GH_TOKEN: ${{ steps.activity-credential.outputs.token }}
GH_TOKEN: ${{ steps.activity-credential.outputs.token }}
REPORT_MAX_GITHUB_API_RATE_LIMIT: ${{ (vars.GH_AW_GITHUB_AUTH_MODE == '' || vars.GH_AW_GITHUB_AUTH_MODE == 'workflow-token') && '-100' || '-2000' }}
CAO_OPERATIONAL_VALUE_GH_TOKEN: ""
GH_TOKEN: ""
REPORT_TARGET_REPOSITORIES_JSON: ${{ toJSON(matrix.repositories) }}
REPORT_GH_AW_LOGS_SHARDS: ${{ runner.temp }}/cao-activity-partial/${{ matrix.artifact }}/gh-aw-logs-shards
REPORT_INVENTORY_SOURCES: ${{ runner.temp }}/cao-activity-partial/${{ matrix.artifact }}/inventory-sources.json
REPORT_VALUE_SEED_SHARDS: ${{ runner.temp }}/cao-activity/gh-aw-logs-shards
Expand All @@ -368,47 +372,41 @@ jobs:
rich_seed="$REPORT_VALUE_SEED_SHARDS/workflow-values-$ACTIVITY_ARTIFACT.jsonl"
rich_output="$REPORT_GH_AW_LOGS_SHARDS/workflow-values-$ACTIVITY_ARTIFACT.jsonl"
if [[ -f "$rich_seed" ]]; then cp "$rich_seed" "$rich_output"; fi
health_seed="$REPORT_VALUE_SEED_SHARDS/workflow-values-$ACTIVITY_ARTIFACT-collections.jsonl"
health_output="$REPORT_GH_AW_LOGS_SHARDS/workflow-values-$ACTIVITY_ARTIFACT-collections.jsonl"
if [[ -f "$health_seed" ]]; then cp "$health_seed" "$health_output"; fi
proof_seed="$RUNNER_TEMP/cao-activity/workflow-value-workspaces/$ACTIVITY_ARTIFACT"
proof_output="$RUNNER_TEMP/cao-activity-partial/$ACTIVITY_ARTIFACT/workflow-value-workspaces/$ACTIVITY_ARTIFACT"
mkdir -p "$proof_output"
if [[ -d "$proof_seed" ]]; then cp -R "$proof_seed/." "$proof_output/"; fi
proof_staging="$RUNNER_TEMP/cao-value-proof-staging/$ACTIVITY_ARTIFACT"
if [[ -d "$proof_seed" ]]; then
mkdir -p "$proof_staging" "$(dirname "$proof_output")"
cp -R "$proof_seed/." "$proof_staging/"
mv "$proof_staging" "$proof_output"
fi
retained_values="$REPORT_VALUE_SEED_SHARDS/operational-values-$ACTIVITY_ARTIFACT.jsonl"
value_output="$REPORT_GH_AW_LOGS_SHARDS/operational-values-$ACTIVITY_ARTIFACT.jsonl"
if [[ -f "$retained_values" ]]; then
cp "$retained_values" "$value_output"
node activity/cao.mjs ingest-jsonl \
--database "$ACTIVITY_DATABASE" \
--input "$value_output" \
--retention-days 30 \
--run-retention-days 30
fi
if [[ "$REPORT_FAST_MODE" == "true" || "$REPORT_RATE_LIMITED" == "true" || "$REPORT_PUBLIC_ONLY" == "true" ]]; then
echo "Fast, public-only, or limited API mode: retained operational-value history; skipping new computation."
exit 0
fi
reason=shared-budget-unavailable
if [[ "$REPORT_FAST_MODE" == "true" ]]; then reason=fast-mode; fi
if [[ "$REPORT_PUBLIC_ONLY" == "true" ]]; then reason=public-only; fi
if [[ "$REPORT_RATE_LIMITED" == "true" ]]; then reason=api-budget; fi
repository_args=()
while IFS= read -r target_repository; do
repository_args+=(--repository "$target_repository")
done < <(jq -r --arg owner "$ACTIVITY_OWNER" \
'.repositories.rows[]
| select((.organization | ascii_downcase) == ($owner | ascii_downcase))
| "\(.organization)/\(.repository)"' \
"$REPORT_INVENTORY_SOURCES")
if ! node activity/cao.mjs operational-value \
--database "$ACTIVITY_DATABASE" \
--root . \
--output "$REPORT_GH_AW_LOGS_SHARDS/operational-values-$ACTIVITY_ARTIFACT.jsonl" \
--retention-days 365 \
"${repository_args[@]}" \
--max-github-api-rate-limit "$REPORT_MAX_GITHUB_API_RATE_LIMIT"; then
echo "::warning::Operational value collection failed for $ACTIVITY_OWNER; continuing activity indexing."
fi
if ! node activity/workflow-values.mjs \
done < <(jq -r --arg owner "$ACTIVITY_OWNER" '.[] | "\($owner)/\(.)"' <<< "$REPORT_TARGET_REPOSITORIES_JSON")
node activity/workflow-values.mjs \
--root . --policy .github/workflows/cao.json \
--database "$ACTIVITY_DATABASE" --output "$rich_output" \
--workspace "$proof_output" "${repository_args[@]}"; then
echo "::warning::Opted-in workflow Value collection failed; retaining accepted history."
fi
--healthOutput "$health_output" --inventory "$REPORT_INVENTORY_SOURCES" \
--defer "$reason" --workspace "$proof_output" "${repository_args[@]}" \
--localRequest "$REPORT_GH_AW_LOGS_SHARDS/workflow-value-local.json" \
--snapshot "$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"

- name: Report collection-health publication failure
if: ${{ steps.value-health.outcome == 'failure' }}
run: echo "::warning::Value health could not be refreshed; retained evidence and its original dates are unchanged. Activity publication continues."

- name: Recheck public-only repository boundary before publication
if: ${{ matrix.publicOnly == true }}
Expand Down Expand Up @@ -555,6 +553,30 @@ jobs:
--input-dir "$RUNNER_TEMP/cao-activity/gh-aw-logs-shards" \
--retention-days 30 --max-bytes "$REPORT_MAX_CACHE_BYTES"

- name: Restore previously completed local Value computation
if: ${{ inputs.recycle-only != true }}
timeout-minutes: 1
continue-on-error: true
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ runner.temp }}/cao-local-value/operational-values.jsonl
key: cao-value-local-v1-lookup-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: cao-value-local-v1-

- name: Retain completed local values within reviewed scope
id: local-value-import
if: ${{ inputs.recycle-only != true }}
timeout-minutes: 1
continue-on-error: true
run: |
node activity/local-values.mjs retain --policy .github/workflows/cao.json \
--input "$RUNNER_TEMP/cao-local-value/operational-values.jsonl" \
--output "$RUNNER_TEMP/cao-activity/gh-aw-logs-shards/operational-values-local.jsonl"

- name: Report optional local Value import failure
if: ${{ steps.local-value-import.outcome == 'failure' }}
run: echo "::warning::Completed local Value results could not be imported; accepted evidence is unchanged. Activity publication continues."

- name: Rebuild recycled snapshot projection from retained evidence
if: ${{ inputs.recycle-only == true }}
run: node activity/recycle-snapshot.mjs reset "$RUNNER_TEMP/cao-activity"
Expand Down
74 changes: 74 additions & 0 deletions .github/workflows/cao-value-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: CAO Local Value

on:
workflow_run: # zizmor: ignore[dangerous-triggers] - Only successful default-branch Activity snapshots are read; code and policy come from the trusted workflow revision.
workflows: [CAO Activity]
types: [completed]

permissions:
contents: read
actions: read

concurrency:
group: central-agentic-ops-local-value
cancel-in-progress: false

jobs:
compute:
if: >-
github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.head_branch == github.event.repository.default_branch
&& github.event.workflow_run.head_repository.id == github.event.repository.id
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout trusted Value source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.workflow_sha }}
persist-credentials: false

- name: Set up CAO activity runtime
uses: ./.github/actions/setup-cao-runtime # zizmor: ignore[self-repository] - local action is checked out at github.workflow_sha.
with:
bundle: activity

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24

- name: Download the already-published Activity snapshot
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: cao-activity-index
path: ${{ runner.temp }}/cao-activity
repository: ${{ github.repository }}
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}

- name: Restore retained local Value history
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ runner.temp }}/cao-local-value/operational-values.jsonl
key: cao-value-local-v1-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
restore-keys: cao-value-local-v1-

- name: Compute bounded local values without target credentials
timeout-minutes: 3
env:
ACTIVITY_SNAPSHOT: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
GH_TOKEN: ""
GITHUB_TOKEN: ""
CAO_OPERATIONAL_VALUE_GH_TOKEN: ""
run: |
node activity/local-values.mjs compute \
--root . --policy .github/workflows/cao.json \
--snapshot "$RUNNER_TEMP/cao-activity" --snapshotKey "$ACTIVITY_SNAPSHOT" \
--output "$RUNNER_TEMP/cao-local-value/operational-values.jsonl"

- name: Cache completed values for a later Activity snapshot
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ runner.temp }}/cao-local-value/operational-values.jsonl
key: cao-value-local-v1-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
4 changes: 4 additions & 0 deletions .github/workflows/shared/materialize-cao.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,11 @@ export function verifyCaoRuntime(bundle, repositoryRoot = process.cwd()) {
activity: [
'activity/cao.mjs',
'activity/workflow-values.mjs',
'activity/workflow-value-history.mjs',
'activity/workflow-value-status.mjs',
'activity/local-values.mjs',
'dashboard/site/src/data/model/operational-value-evidence.js',
'dashboard/site/src/data/model/workflow-value-collection.js',
'activity/cli/activity-stats.mjs',
'activity/cli/cache.mjs',
'activity/cli/dashboard.mjs',
Expand Down
31 changes: 27 additions & 4 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ deduplicates GitHub webhooks, and atomically replaces the current Postgres
sources and state. `server/spec/storage.tsp` owns the fresh-only physical
Postgres contract. Its emitter generates the standalone
`server/internal/postgresx/schema.sql` and matching Go column bindings.
Nineteen canonical collections map one-to-one to entity tables containing only
Canonical collections map one-to-one to entity tables containing only
query-consumed fields and identity/storage keys. Scalars use native SQL types;
required structured fields use entity-owned relational child values, not
JSON, JSONB, or serialized row documents. Scalar entity rows are never duplicated
Expand All @@ -294,6 +294,12 @@ source metadata without asserting complete coverage or current freshness.
Counts, comparisons, movement and report presentation remain declarative
queries; importing evidence grants no target authority and navigation never
acquires evidence.
Separate `workflowValueCollections` records retain reviewed registrations and
bounded collection decisions through the same producer, normalization and
browser/SQLite/PostgreSQL paths. Decisions do not advance actual-attempt,
accepted-capture or evidence-cutoff clocks. Declarative Value queries expose
enabled, disabled and unconfigured health before a first report; stale/conflicting
decisions and regressed attempt history fail closed.
Explicit `gh cao value publish` writes one validated original packet through
administrator-only `POST /api/v1/value` into PostgreSQL. The namespace writer
lock serializes publication with snapshot replacement; typed ownership metadata
Expand Down Expand Up @@ -731,9 +737,26 @@ Successful-Run value computations keep produced safe outputs, native
operational-grader measurements, and efficiency evidence separate; they do not
turn runtime success or output creation into accepted value.
Package-level `operational-value.mjs` programs compute repository-scoped metric
records through `cao operational-value`. Activity appends those timestamped
records to authoritative JSONL before the canonical Operational Value collection
is rebuilt in SQLite, IndexedDB, and the hosted Postgres entity store.
records through `cao operational-value`. Automatic remote acquisition remains
deferred until shared per-request API admission can protect ordinary Activity
against concurrent credential consumers; separate workers are not separate quota.
Activity publishes health and retains accepted packets/proof without remote
Value acquisition. Health reconciles the latest decision and actual attempt
independently of arrival order; unknown attempts never erase known history.
Bounded cache-only/local computation remains independent:
Actions runs it only after a successful eligible Activity snapshot, while the
hosted worker uses its own single-owner lease and verified private snapshot.
Both use the reviewed local-only standard adapter, explicit local-capability
modules or exact versioned cache reuse, no target credentials or automatic
backfill, and a two-minute batch/thirty-second adapter budget. Scope, explicit
disables, fast, public-only and API-limited gates remain enforced. A later
ordinary snapshot imports completed results; optional work does not acquire
the projection lease, hold a long core evidence lock, or initiate a
compute/publication retry loop. Hosted final publication rechecks enrollment
and the starting generation, manifest, policy and retained-result identities;
changed inputs defer publication rather than overwrite concurrent evidence.
Normalized timestamped records then rebuild
the canonical Operational Value collection in every storage profile.
Package-level `problem-clustering.mjs` programs read a private Activity SQLite
snapshot and emit bounded problem records with actionable fix prompts through
`cao cluster-problems`.
Expand Down
Loading
Loading