@bongos/core 1.21.176 → 1.21.178
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +186 -496
- package/.claude/skills/planning-session/SKILL.md +2 -2
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +0 -6
- package/clients/bongos-client/index.cjs +0 -6
- package/clients/bongos-client/index.d.ts +0 -6
- package/clients/bongos-client/index.mjs +0 -6
- package/config/scheduled-routines.json +3 -8
- package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +3 -2
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +1 -1
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +1 -1
- package/docs/api/openapi.json +1 -87
- package/docs/api-reference.md +1 -8
- package/docs/canonical-permissions.md +4 -4
- package/docs/copy-inventory.md +25 -39
- package/docs/copy-registry.json +73 -200
- package/docs/file-map.md +6 -11
- package/docs/handoff-template.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +2 -2
- package/docs/onboarding/diagrams/README.md +3 -7
- package/docs/onboarding/diagrams/assertions.json +3 -3
- package/docs/page-inventory.json +4 -7
- package/docs/page-readings.json +1520 -1569
- package/docs/recipes/ops-gotchas.md +3 -1
- package/modules/autonomy/db.js +9 -0
- package/modules/autonomy/module.json +2 -2
- package/modules/autonomy/pollers/daily-checks.js +262 -0
- package/modules/autonomy/pollers/routine-timers.js +7 -5
- package/modules/autonomy/rank-roles.js +64 -0
- package/modules/autonomy/server-schedule.js +37 -0
- package/modules/government/catalog.js +10 -7
- package/modules/government/decision-board.js +0 -1
- package/modules/government/decision-rules.js +0 -1
- package/modules/government/docket.js +0 -1
- package/modules/government/protected-surfaces.js +1 -1
- package/modules/government/protected-surfaces.json +2 -3
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.css +4 -26
- package/modules/hall-ui/public/gate.html +12 -27
- package/modules/hall-ui/public/gate.js +26 -176
- package/modules/hall-ui/public/gate.states.json +4 -22
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/street-card.js +5 -9
- package/modules/hall-ui/public/street-desk.js +2 -2
- package/modules/hall-ui/public/street-inside.js +1 -1
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/lifecycle/matter-acts.js +0 -6
- package/modules/lifecycle/module.json +0 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +36 -0
- package/scripts/bongos/adopt-preflight.js +3 -3
- package/scripts/bongos/api-alias-caller-scan.js +2 -2
- package/scripts/bongos/audit-authorship.sh +1 -8
- package/scripts/bongos/audit-rank-roles.js +15 -56
- package/scripts/bongos/bump-version.js +46 -9
- package/scripts/bongos/codemod-rename-scripts-gds.js +0 -1
- package/scripts/bongos/diagram-facts.js +1 -1
- package/scripts/bongos/do-api.js +1 -1
- package/scripts/bongos/doc-cli-guard.js +2 -0
- package/scripts/bongos/doc-control-claims.js +2 -2
- package/scripts/bongos/fitness-checks-identity.js +4 -15
- package/scripts/bongos/gen-diagrams.js +3 -3
- package/scripts/bongos/government-walk.js +3 -9
- package/scripts/bongos/hard-floor.js +52 -0
- package/scripts/bongos/publish-manifest.js +3 -3
- package/scripts/bongos/pulse.js +5 -3
- package/scripts/bongos/routine-schedule.js +38 -3
- package/scripts/bongos/run-unit-tests.js +17 -5
- package/scripts/bongos/ship-regen.js +2 -3
- package/scripts/gds/{goal-names.js → hard-floor.js} +3 -4
- package/scripts/security/dep-audit.js +50 -124
- package/src/bongos/module-scope-map.js +2 -2
- package/src/bongos/permission-path-check.js +1 -1
- package/src/bongos/route-rank-check.js +1 -8
- package/src/bongos/routes.js +1 -2
- package/src/module-api.js +1 -1
- package/src/modules.js +1 -1
- package/tests/acclaim_feed_evidence_db.mjs +42 -23
- package/tests/adr_0089_scope.mjs +7 -5
- package/tests/autonomy_daily_checks.mjs +201 -0
- package/tests/bump_version.mjs +52 -18
- package/tests/claude_materialize.mjs +12 -12
- package/tests/copy_desk_flags_db.mjs +22 -14
- package/tests/effective_visibility_predicate.mjs +0 -18
- package/tests/gating_review_retired.mjs +1 -1
- package/tests/government_act_alone_buildings.mjs +1 -3
- package/tests/government_board_membership_db.mjs +35 -22
- package/tests/government_protected_surfaces.mjs +29 -13
- package/tests/governor_docket.mjs +0 -2
- package/tests/governor_matter_acts.mjs +6 -8
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_matter_card.mjs +2 -2
- package/tests/hall_oversight_world.mjs +16 -12
- package/tests/hall_street_desk.mjs +4 -3
- package/tests/hall_street_inside.mjs +1 -1
- package/tests/helpers.mjs +34 -0
- package/tests/howto_state.mjs +33 -56
- package/tests/howto_state_db.mjs +68 -0
- package/tests/idea_credit_streams_db.mjs +11 -3
- package/tests/integration_parked_ratchet.mjs +7 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/page_notes_db.mjs +12 -8
- package/tests/page_tweak_no_rearm.mjs +50 -0
- package/tests/publish_manifest.mjs +64 -0
- package/tests/routine_capture_kind.mjs +18 -7
- package/tests/routine_schedule.mjs +42 -0
- package/tests/search_isolation.mjs +5 -103
- package/tests/search_isolation_db.mjs +140 -0
- package/tests/session_earnings_mirror_db.mjs +2 -0
- package/tests/workflow_exec_paths.mjs +9 -11
- package/modules/lifecycle/routes/gate-approvals.js +0 -299
- package/scripts/bongos/artifact-staleness.js +0 -267
- package/scripts/bongos/ci-grade.js +0 -82
- package/scripts/bongos/classify-discipline.js +0 -153
- package/scripts/bongos/codemod-rename-src-gds.js +0 -145
- package/scripts/bongos/coreB-carve-proof.sh +0 -725
- package/scripts/bongos/gate-review.js +0 -330
- package/scripts/bongos/goal-names.js +0 -102
- package/scripts/bongos/rename-goals.js +0 -109
- package/scripts/bongos/seed-adopt-tasks.js +0 -75
- package/scripts/bongos/seed-api-contract-goal-tasks.js +0 -141
- package/scripts/bongos/seed-board-room-tasks.js +0 -196
- package/scripts/bongos/seed-bongos-app-tasks.js +0 -294
- package/scripts/bongos/seed-bongos-coreB-tranche1-tasks.js +0 -81
- package/scripts/bongos/seed-bongos-coreB-tranche2-tasks.js +0 -100
- package/scripts/bongos/seed-bongos-goal-hierarchy-tasks.js +0 -307
- package/scripts/bongos/seed-bongos-module-system-tasks.js +0 -346
- package/scripts/bongos/seed-bongos-tasks.js +0 -214
- package/scripts/bongos/seed-bongos-v1-seo-tasks.js +0 -104
- package/scripts/bongos/seed-bv1-community-tasks.js +0 -131
- package/scripts/bongos/seed-bv1-privacy-invites-tasks.js +0 -133
- package/scripts/bongos/seed-bv1-profile-page-tasks.js +0 -161
- package/scripts/bongos/seed-bv1-r01-delta-tasks.js +0 -154
- package/scripts/bongos/seed-bv2-tweak-mode-tasks.js +0 -277
- package/scripts/bongos/seed-gds-v4-tasks.js +0 -624
- package/scripts/bongos/seed-governance-tasks.js +0 -340
- package/scripts/bongos/seed-medusa-tasks.js +0 -440
- package/scripts/bongos/seed-module-upstreaming.js +0 -219
- package/scripts/bongos/seed-productization-tasks.js +0 -235
- package/scripts/bongos/seed-project-admin-console-tasks.js +0 -88
- package/scripts/bongos/seed-provisioning-tasks.js +0 -83
- package/scripts/bongos/seed-strict-versioning-tasks.js +0 -275
- package/scripts/bongos/seed-v2-world-tasks.js +0 -283
- package/scripts/gds/artifact-staleness.js +0 -7
- package/scripts/gds/ci-grade.js +0 -7
- package/scripts/gds/classify-discipline.js +0 -7
- package/scripts/gds/codemod-rename-src-gds.js +0 -7
- package/scripts/gds/coreB-carve-proof.sh +0 -8
- package/scripts/gds/gate-review.js +0 -7
- package/scripts/gds/rename-goals.js +0 -7
- package/scripts/gds/seed-adopt-tasks.js +0 -7
- package/scripts/gds/seed-api-contract-goal-tasks.js +0 -7
- package/scripts/gds/seed-board-room-tasks.js +0 -6
- package/scripts/gds/seed-bongos-app-tasks.js +0 -7
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +0 -7
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +0 -7
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +0 -7
- package/scripts/gds/seed-bongos-module-system-tasks.js +0 -7
- package/scripts/gds/seed-bongos-tasks.js +0 -7
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +0 -6
- package/scripts/gds/seed-bv1-community-tasks.js +0 -6
- package/scripts/gds/seed-bv1-privacy-invites-tasks.js +0 -6
- package/scripts/gds/seed-bv1-profile-page-tasks.js +0 -6
- package/scripts/gds/seed-bv1-r01-delta-tasks.js +0 -6
- package/scripts/gds/seed-bv2-tweak-mode-tasks.js +0 -6
- package/scripts/gds/seed-gds-v4-tasks.js +0 -7
- package/scripts/gds/seed-governance-tasks.js +0 -7
- package/scripts/gds/seed-medusa-tasks.js +0 -7
- package/scripts/gds/seed-module-upstreaming.js +0 -7
- package/scripts/gds/seed-productization-tasks.js +0 -7
- package/scripts/gds/seed-project-admin-console-tasks.js +0 -6
- package/scripts/gds/seed-provisioning-tasks.js +0 -7
- package/scripts/gds/seed-strict-versioning-tasks.js +0 -7
- package/scripts/gds/seed-v2-world-tasks.js +0 -7
- package/scripts/migrate_cost_ledger.sh +0 -178
- package/tests/artifact_staleness.mjs +0 -207
- package/tests/gate_approvals.mjs +0 -611
- package/tests/gate_review.mjs +0 -152
- package/tests/goal_names.mjs +0 -120
- package/tests/newcomer_generator.mjs +0 -48
|
@@ -216,11 +216,13 @@ Inside the namespace the file lists as `---------- 1 root root 0`. The runner un
|
|
|
216
216
|
|
|
217
217
|
## Bongos build pipeline
|
|
218
218
|
|
|
219
|
-
###
|
|
219
|
+
### The deterministic routines run as in-process server timers, not external cron
|
|
220
220
|
|
|
221
221
|
- **What:** `peer-vote-tally` (weekly) runs inside the always-on Node process via the autonomy module's poller `modules/autonomy/pollers/routine-timers.js` (ADR 0078 amendment, task 1382; carved into `modules/autonomy/` at BV1.R83) — no `claude -p`, no external cron, no token. The loader's `startModulePollers` starts it from `src/platform-server.js` at boot (task 1002419 — before that fix the call site had been lost with the retired `server.js` and no module poller started at all); it reaches `tallyPeerVotes` through the doorway. It's a no-op anywhere the DB is unreachable (builder machines), so it only does work on the prod droplet.
|
|
222
222
|
- **Disable / move it out-of-process:** set `ROUTINE_TIMERS_DISABLED=1` in `/etc/example/web-source.env` and restart. Also set it on any **second** always-on instance that shares the `example` DB so the tally can't double-fold (the `FOR UPDATE OF pv` row lock is the hard guard if you forget).
|
|
223
223
|
- **Run it manually now:** `node scripts/bongos/run-routine.js peer-vote-tally` (the dispatcher front door still works). The cadence is boot-anchored ("~90s after boot, then weekly"), not a wall-clock Monday job — frequent deploys re-anchor it, which is harmless (the tally is idempotent).
|
|
224
|
+
- **The daily checks** (task 1004878): `rank-roles-audit` and `dep-audit-nightly` run from the sibling poller `modules/autonomy/pollers/daily-checks.js`. Unlike peer-vote it is **not** boot-anchored — deploys restart the server many times a day — so it ticks hourly and runs a check only when that check's last `autonomy_runs` row is ~a day old. Findings go to the idea inbox, once per distinct set (the row stores a fingerprint; an identical result is recorded `unchanged`). See what ran without SSH: `GET /autonomy/runs?routine=dep-audit-nightly`. Same kill switch (`ROUTINE_TIMERS_DISABLED=1`). Which routines the server fires, and how often, is ONE table — `modules/autonomy/server-schedule.js` — read by both the pollers and the merge-gate check `scripts/bongos/routine-schedule.js`.
|
|
225
|
+
- **`dep-audit-nightly` audits the INSTANCE root** (`resolveInstanceRoot()`, the service's working directory), so it needs `npm` on the service's PATH and a `package-lock.json` there. Without either it records a `no-go` row with the reason — never a clean pass.
|
|
224
226
|
|
|
225
227
|
### A ship was graded FAIL but the work is genuinely good (grader false-negative)
|
|
226
228
|
|
package/modules/autonomy/db.js
CHANGED
|
@@ -62,6 +62,14 @@ async function readAutonomyRuns({ routine = null, limit = 20 } = {}) {
|
|
|
62
62
|
return rows.map((r) => ({ ...r, id: String(r.id) }));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// readRankRoster — every builder's id, login, rank and status, for the daily
|
|
66
|
+
// rank-roles check (task 1004878). No WHERE on purpose: an INACTIVE builder on a
|
|
67
|
+
// legacy rank is exactly the row a future CHECK-narrowing migration would fail on.
|
|
68
|
+
async function readRankRoster() {
|
|
69
|
+
const { rows } = await pool.query('SELECT id, github_login, rank, status FROM builders ORDER BY id');
|
|
70
|
+
return rows;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
// ── the Autobongos fence (task 1003908, ADR 0307 §6) ─────────────────────────
|
|
66
74
|
//
|
|
67
75
|
// These back the one control surface the runner host cannot write to. Everything
|
|
@@ -350,6 +358,7 @@ module.exports = {
|
|
|
350
358
|
recordAutonomyRun,
|
|
351
359
|
lastAutonomyHead,
|
|
352
360
|
readAutonomyRuns,
|
|
361
|
+
readRankRoster,
|
|
353
362
|
recordRunnerHeartbeat,
|
|
354
363
|
readRunnerHeartbeats,
|
|
355
364
|
readFence,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"key": "autonomy",
|
|
3
3
|
"title": "Autonomy & MAS (conductor · architect · gate · routine timers)",
|
|
4
|
-
"description": "The Multi-Agent System's autonomy surfaces (ADR 0024): the $0 deterministic autonomy gate (System 3 — a precheck route + the gate CLI, inert until OTB_AUTONOMY_ENABLED=1), the conductor mid-flight dispatch CLI (Phase 4), the Architect bounded first-principles --scope audit CLI (Phase 5), and the in-process routine timers (System 4 / ADR 0078 amendment — the weekly peer-vote-tally poller). A core-domain module carved out of the GDS core in BV1.R83 (ADR 0093 §1); default-on because the autonomy machinery + the durable routine timer ship to every instance (both are inert/no-op until armed or the pool is reachable). Cross-cutting: it OBSERVES lifecycle events. The routine timer reaches the lifecycle `tallyPeerVotes` capability through the published doorway (a tracked lifecycle leak until R86), never a core require; the precheck route + the gate/conductor/architect CLIs travel with this domain (the CLIs stay under scripts/, outside the kernel boundary, so they may still read the ideas/economy/lifecycle signals + the grader directly). Registers no port — core never calls back in.",
|
|
4
|
+
"description": "The Multi-Agent System's autonomy surfaces (ADR 0024): the $0 deterministic autonomy gate (System 3 — a precheck route + the gate CLI, inert until OTB_AUTONOMY_ENABLED=1), the conductor mid-flight dispatch CLI (Phase 4), the Architect bounded first-principles --scope audit CLI (Phase 5), and the in-process routine timers (System 4 / ADR 0078 amendment — the weekly peer-vote-tally poller, and since task 1004878 the daily rank-roles and dependency-audit checks). A core-domain module carved out of the GDS core in BV1.R83 (ADR 0093 §1); default-on because the autonomy machinery + the durable routine timer ship to every instance (both are inert/no-op until armed or the pool is reachable). Cross-cutting: it OBSERVES lifecycle events. The routine timer reaches the lifecycle `tallyPeerVotes` capability through the published doorway (a tracked lifecycle leak until R86), never a core require; the precheck route + the gate/conductor/architect CLIs travel with this domain (the CLIs stay under scripts/, outside the kernel boundary, so they may still read the ideas/economy/lifecycle signals + the grader directly). Registers no port — core never calls back in.",
|
|
5
5
|
"version": "1.0.0",
|
|
6
6
|
"coreVersion": "^1.11.0",
|
|
7
7
|
"category": { "parent": "work" },
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"maintenance": { "status": "core-maintained" },
|
|
10
10
|
"contributes": {
|
|
11
11
|
"routes": ["autonomy"],
|
|
12
|
-
"pollers": ["routine-timers"]
|
|
12
|
+
"pollers": ["routine-timers", "daily-checks"]
|
|
13
13
|
},
|
|
14
14
|
"provides": [],
|
|
15
15
|
"consumes": []
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/autonomy/pollers/daily-checks.js — the deterministic daily checks, fired
|
|
4
|
+
// by the always-on server itself (task 1004878).
|
|
5
|
+
//
|
|
6
|
+
// WHY IN-PROCESS. Every one of these routines declared a cadence and nothing fired
|
|
7
|
+
// it: the repo's only scheduler was .github/workflows/, whose minutes are billable
|
|
8
|
+
// and already oversubscribed, and the operator-side cron the SKILL.md files kept
|
|
9
|
+
// asking for was never written. The routine-timers poller beside this one solved
|
|
10
|
+
// the same problem for peer-vote-tally (ADR 0078 amendment) — an in-process timer
|
|
11
|
+
// costs nothing and cannot be forgotten by an operator. This is that pattern for
|
|
12
|
+
// the daily checks.
|
|
13
|
+
//
|
|
14
|
+
// THE CHECKS (the rows of ../server-schedule.js with poller 'daily-checks'):
|
|
15
|
+
// - rank-roles-audit — every builder on one of the live ranks (task 1002873).
|
|
16
|
+
// Reads the roster straight from this instance's DB, so it
|
|
17
|
+
// needs no CLI session — the reason the script never ran
|
|
18
|
+
// unattended.
|
|
19
|
+
// - dep-audit-nightly — `npm audit` of the DEPLOYED instance (its own root and
|
|
20
|
+
// lockfile, not a repo checkout), high/critical only.
|
|
21
|
+
//
|
|
22
|
+
// DAILY BY THE CLOCK, NOT BY THE BOOT. The server restarts on every deploy, and a
|
|
23
|
+
// boot-anchored timer would re-run (and re-file) on each one. So the timer only
|
|
24
|
+
// ticks hourly and asks the ledger: a check runs when its last `autonomy_runs` row
|
|
25
|
+
// is (nearly) a day old. The same rows are what GET /autonomy/runs shows, so
|
|
26
|
+
// "did the daily check run, and what did it find" is answerable from a laptop.
|
|
27
|
+
//
|
|
28
|
+
// ONE IDEA PER DISTINCT FINDING SET. A finding is filed into the idea inbox once;
|
|
29
|
+
// the run row stores a fingerprint of what was found, and the next day's identical
|
|
30
|
+
// result is recorded as `unchanged` instead of filed again. A changed set (a new
|
|
31
|
+
// vulnerability, a second bad rank) files again. A clean run clears it, so a
|
|
32
|
+
// regression after a fix is a fresh idea.
|
|
33
|
+
//
|
|
34
|
+
// Mirrors routine-timers.js (but logs through api.logger, not console): kill switch ROUTINE_TIMERS_DISABLED, unref'd timers,
|
|
35
|
+
// best-effort (a throw logs and waits for the next tick — never crashes the
|
|
36
|
+
// server), pure w.r.t. injected deps so it unit-tests without a database, and a
|
|
37
|
+
// quiet no-op anywhere the database is unreachable (builder laptops).
|
|
38
|
+
|
|
39
|
+
const { execFile } = require('node:child_process');
|
|
40
|
+
const crypto = require('node:crypto');
|
|
41
|
+
const api = require('../../../src/module-api');
|
|
42
|
+
const autonomyDb = require('../db');
|
|
43
|
+
const rankRoles = require('../rank-roles');
|
|
44
|
+
const depAudit = require('../../../scripts/security/dep-audit');
|
|
45
|
+
const { SERVER_ROUTINES, periodMs } = require('../server-schedule');
|
|
46
|
+
|
|
47
|
+
const log = api.logger('autonomy');
|
|
48
|
+
|
|
49
|
+
const ROUTINES = SERVER_ROUTINES.filter((r) => r.poller === 'daily-checks').map((r) => r.routine);
|
|
50
|
+
|
|
51
|
+
const TICK_MS = Number(process.env.ROUTINE_DAILY_CHECKS_TICK_MS) || 60 * 60_000;
|
|
52
|
+
// Later than peer-vote's 90s so the two first ticks never land together.
|
|
53
|
+
const FIRST_MS = Number(process.env.ROUTINE_DAILY_CHECKS_FIRST_MS) || 3 * 60_000;
|
|
54
|
+
const JITTER_MS = 60_000;
|
|
55
|
+
// A check is due this long BEFORE a full period has passed. Hourly ticks land up to
|
|
56
|
+
// an hour late; without the slack each day's run would slide an hour later than the
|
|
57
|
+
// last. With it, runs stay between 23.5 and 24.5 hours apart.
|
|
58
|
+
const DUE_SLACK_MS = 30 * 60_000;
|
|
59
|
+
// npm audit is a network call to the registry; one stuck call must not hold the
|
|
60
|
+
// next day's check hostage.
|
|
61
|
+
const NPM_AUDIT_TIMEOUT_MS = 5 * 60_000;
|
|
62
|
+
|
|
63
|
+
// ── the checks ─────────────────────────────────────────────────────────────────
|
|
64
|
+
// Each returns { keys, idea } — `keys` identify the findings (empty = clean) and
|
|
65
|
+
// feed the fingerprint; `idea` is what gets filed. A check that cannot run THROWS:
|
|
66
|
+
// "could not check" must never be recorded as clean.
|
|
67
|
+
|
|
68
|
+
async function checkRankRoles(deps) {
|
|
69
|
+
const rows = await deps.db.readRankRoster();
|
|
70
|
+
const offenders = rankRoles.findRanksWithoutRoles(rows, deps.liveRanks || api.LIVE_RANK_LADDER);
|
|
71
|
+
return {
|
|
72
|
+
keys: offenders.map((o) => `${o.id}:${o.rank}`),
|
|
73
|
+
idea: { ...rankRoles.ideaFor(offenders, { filedBy: "the server's daily rank-roles check" }), kind: 'bug' },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The async spawn. execFile reports npm's own exit 1 (findings present) as an error
|
|
78
|
+
// too, so only a missing binary or a timeout counts as "could not run".
|
|
79
|
+
function npmAudit(cwd) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
execFile('npm', depAudit.NPM_AUDIT_ARGS, { cwd, maxBuffer: 32 * 1024 * 1024, timeout: NPM_AUDIT_TIMEOUT_MS },
|
|
82
|
+
(err, stdout, stderr) => {
|
|
83
|
+
const failed = err && (err.code === 'ENOENT' || err.killed);
|
|
84
|
+
resolve({ stdout, stderr, error: failed ? (err.code === 'ENOENT' ? 'npm not found' : 'npm audit timed out') : null });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function checkDepAudit(deps) {
|
|
90
|
+
const cwd = deps.cwd || api.resolveInstanceRoot();
|
|
91
|
+
const out = await (deps.npmAudit || npmAudit)(cwd);
|
|
92
|
+
if (out.error) throw new Error(out.error);
|
|
93
|
+
const res = depAudit.parseNpmAudit(out.stdout, out.stderr);
|
|
94
|
+
if (!res.ok) throw new Error(res.raw_error);
|
|
95
|
+
const f = res.findings;
|
|
96
|
+
return {
|
|
97
|
+
keys: f.map((x) => `${x.package}:${x.id || x.summary}`),
|
|
98
|
+
idea: {
|
|
99
|
+
kind: 'infra',
|
|
100
|
+
title: `[dep-audit] ${f.length} high/critical ${f.length === 1 ? 'vulnerability' : 'vulnerabilities'} in this instance's dependencies`,
|
|
101
|
+
bodyMd: [
|
|
102
|
+
`The server's daily \`npm audit\` of this instance (${cwd}) found ${f.length} high/critical advisory(ies):`,
|
|
103
|
+
'',
|
|
104
|
+
...f.map((x) => `- **${x.package}** (${x.severity}) — ${x.summary}${x.url ? ` — ${x.url}` : ''}. Fix: ${x.fix}.`),
|
|
105
|
+
'',
|
|
106
|
+
'Most of these arrive through the core package, so the fix is usually a core release',
|
|
107
|
+
'that bumps the dependency, then moving this instance onto it.',
|
|
108
|
+
'',
|
|
109
|
+
'Filed by modules/autonomy/pollers/daily-checks.js (task 1004878). The same set is not',
|
|
110
|
+
're-filed tomorrow; a changed set is.',
|
|
111
|
+
].join('\n'),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const CHECKS = {
|
|
117
|
+
'rank-roles-audit': checkRankRoles,
|
|
118
|
+
'dep-audit-nightly': checkDepAudit,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// ── the ledger half ────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
const fingerprint = (keys) =>
|
|
124
|
+
crypto.createHash('sha1').update([...keys].sort().join('\n')).digest('hex').slice(0, 12);
|
|
125
|
+
|
|
126
|
+
// The fingerprint the last DELIVERED finding carried, or null. Only a `go` row
|
|
127
|
+
// counts: an error or an undelivered filing in between must not make tomorrow's
|
|
128
|
+
// identical result look new, and must not make it look delivered either.
|
|
129
|
+
function lastFiledFingerprint(rows) {
|
|
130
|
+
const go = (rows || []).find((r) => r.decision === 'go');
|
|
131
|
+
const m = go && /\bfp=([0-9a-f]+)/.exec(go.reason || '');
|
|
132
|
+
return m ? m[1] : null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isDue(rows, routine, now) {
|
|
136
|
+
const last = (rows || [])[0];
|
|
137
|
+
if (!last) return true;
|
|
138
|
+
const at = new Date(last.created_at).getTime();
|
|
139
|
+
if (!Number.isFinite(at)) return true;
|
|
140
|
+
return now - at >= periodMs(routine) - DUE_SLACK_MS;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// One routine, once: run it if it is due, file what it found if that is new, and
|
|
144
|
+
// write the row that says so. NEVER throws.
|
|
145
|
+
async function runCheck(routine, deps = {}) {
|
|
146
|
+
const db = deps.db || autonomyDb;
|
|
147
|
+
const logFn = deps.log || ((m) => log.info(m));
|
|
148
|
+
const errFn = deps.error || ((m) => log.error(m));
|
|
149
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
150
|
+
const record = (decision, reason) =>
|
|
151
|
+
db.recordAutonomyRun({ routine, decision, reason }).catch((e) =>
|
|
152
|
+
errFn(`[daily-checks] ${routine}: could not record the run (${e && e.message})`));
|
|
153
|
+
|
|
154
|
+
let rows;
|
|
155
|
+
try {
|
|
156
|
+
rows = await db.readAutonomyRuns({ routine, limit: 10 });
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return { skipped: 'db_unreachable', error: e && e.message };
|
|
159
|
+
}
|
|
160
|
+
if (!isDue(rows, routine, now)) return { skipped: 'not_due' };
|
|
161
|
+
|
|
162
|
+
let result;
|
|
163
|
+
try {
|
|
164
|
+
result = await CHECKS[routine]({ ...deps, db });
|
|
165
|
+
} catch (e) {
|
|
166
|
+
const msg = (e && e.message) || String(e);
|
|
167
|
+
errFn(`[daily-checks] ${routine}: could not run (${msg})`);
|
|
168
|
+
await record('no-go', `error: ${msg}`.slice(0, 500));
|
|
169
|
+
return { outcome: 'error', error: msg };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!result.keys.length) {
|
|
173
|
+
await record('go', 'clean');
|
|
174
|
+
return { outcome: 'clean' };
|
|
175
|
+
}
|
|
176
|
+
const fp = fingerprint(result.keys);
|
|
177
|
+
const n = result.keys.length;
|
|
178
|
+
if (fp === lastFiledFingerprint(rows)) {
|
|
179
|
+
await record('go', `findings n=${n} fp=${fp} unchanged`);
|
|
180
|
+
return { outcome: 'unchanged', fingerprint: fp };
|
|
181
|
+
}
|
|
182
|
+
const capture = deps.captureIdea || api.resolveOptional('ideas.capture');
|
|
183
|
+
if (typeof capture !== 'function') {
|
|
184
|
+
errFn(`[daily-checks] ${routine}: ${n} finding(s) and no idea inbox to file them in (ideas module off)`);
|
|
185
|
+
await record('no-go', `findings n=${n} fp=${fp} undelivered: no ideas.capture`);
|
|
186
|
+
return { outcome: 'undelivered', fingerprint: fp };
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const idea = await capture({ ...result.idea, filedVia: 'api' });
|
|
190
|
+
logFn(`[daily-checks] ${routine}: ${n} finding(s) filed as idea ${idea && idea.id}`);
|
|
191
|
+
await record('go', `findings n=${n} fp=${fp} idea=${idea && idea.id}`);
|
|
192
|
+
return { outcome: 'filed', fingerprint: fp, ideaId: idea && idea.id };
|
|
193
|
+
} catch (e) {
|
|
194
|
+
const msg = (e && (e.message || e.code)) || String(e);
|
|
195
|
+
errFn(`[daily-checks] ${routine}: ${n} finding(s), filing failed (${msg})`);
|
|
196
|
+
await record('no-go', `findings n=${n} fp=${fp} undelivered: ${msg}`.slice(0, 500));
|
|
197
|
+
return { outcome: 'undelivered', fingerprint: fp, error: msg };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// One tick: every daily routine in turn. Sequential on purpose — npm audit is the
|
|
202
|
+
// heavy one and nothing here is urgent enough to run two at once.
|
|
203
|
+
let running = false;
|
|
204
|
+
let warnedUnreachable = false;
|
|
205
|
+
async function tick(deps = {}) {
|
|
206
|
+
if (running) return [];
|
|
207
|
+
running = true;
|
|
208
|
+
try {
|
|
209
|
+
const out = [];
|
|
210
|
+
for (const routine of ROUTINES) {
|
|
211
|
+
const r = await runCheck(routine, deps);
|
|
212
|
+
if (r.skipped === 'db_unreachable' && !warnedUnreachable) {
|
|
213
|
+
warnedUnreachable = true;
|
|
214
|
+
log.info(`[daily-checks] skipped — database unreachable (${r.error}); a builder machine has nothing to check`);
|
|
215
|
+
}
|
|
216
|
+
out.push({ routine, ...r });
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
} finally {
|
|
220
|
+
running = false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let firstTimer = null;
|
|
225
|
+
let interval = null;
|
|
226
|
+
|
|
227
|
+
function start(deps = {}) {
|
|
228
|
+
if (process.env.ROUTINE_TIMERS_DISABLED === '1') {
|
|
229
|
+
log.info('[daily-checks] disabled (ROUTINE_TIMERS_DISABLED=1)');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (firstTimer || interval) return; // already started
|
|
233
|
+
const fire = () => tick(deps).catch((e) => log.error(`[daily-checks] tick crashed: ${e && e.message}`));
|
|
234
|
+
firstTimer = setTimeout(() => {
|
|
235
|
+
fire();
|
|
236
|
+
interval = setInterval(fire, TICK_MS);
|
|
237
|
+
if (interval.unref) interval.unref();
|
|
238
|
+
}, FIRST_MS + Math.floor(Math.random() * JITTER_MS));
|
|
239
|
+
if (firstTimer.unref) firstTimer.unref();
|
|
240
|
+
log.info(`[daily-checks] started — ${ROUTINES.join(', ')} once a day (checked hourly against the run ledger)`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stop() {
|
|
244
|
+
if (firstTimer) clearTimeout(firstTimer);
|
|
245
|
+
if (interval) clearInterval(interval);
|
|
246
|
+
firstTimer = interval = null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
module.exports = {
|
|
250
|
+
start,
|
|
251
|
+
stop,
|
|
252
|
+
tick,
|
|
253
|
+
runCheck,
|
|
254
|
+
isDue,
|
|
255
|
+
fingerprint,
|
|
256
|
+
lastFiledFingerprint,
|
|
257
|
+
checkRankRoles,
|
|
258
|
+
checkDepAudit,
|
|
259
|
+
ROUTINES,
|
|
260
|
+
TICK_MS,
|
|
261
|
+
DUE_SLACK_MS,
|
|
262
|
+
};
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
// so over-/under-firing is harmless and a second instance can't double-fold.
|
|
39
39
|
//
|
|
40
40
|
// (diagram-drift was intentionally NOT made a timer: all five onboarding diagrams
|
|
41
|
-
// already auto-regenerate from live facts on every deploy
|
|
42
|
-
//
|
|
43
|
-
//
|
|
41
|
+
// already auto-regenerate from live facts on every deploy. Task 1004878 retired
|
|
42
|
+
// the routine outright. The daily checks that ARE timed live beside this file, in
|
|
43
|
+
// daily-checks.js.)
|
|
44
44
|
//
|
|
45
45
|
// Mirrors the publish-reconciler contract: env-overridable interval, an UNREF'd
|
|
46
46
|
// timer (never holds the process open across a systemd restart), best-effort (a
|
|
@@ -57,9 +57,11 @@
|
|
|
57
57
|
// core load at module-discovery time, and so tests that pass deps.db bypass it.
|
|
58
58
|
const api = require('../../../src/module-api');
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
// The period comes from ../server-schedule.js, the table the merge-gate cadence check
|
|
61
|
+
// (scripts/bongos/routine-schedule.js) also reads — so the two cannot disagree.
|
|
62
|
+
const { periodMs } = require('../server-schedule');
|
|
61
63
|
|
|
62
|
-
const PEER_VOTE_INTERVAL_MS = Number(process.env.ROUTINE_PEER_VOTE_MS) ||
|
|
64
|
+
const PEER_VOTE_INTERVAL_MS = Number(process.env.ROUTINE_PEER_VOTE_MS) || periodMs('peer-vote-tally');
|
|
63
65
|
// First-fire delay: let the server finish binding + the other in-process pollers
|
|
64
66
|
// settle before the first tally. Jitter spreads first-fire across a minute so a
|
|
65
67
|
// crash-loop / deploy storm doesn't synchronize every restart's first run.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/autonomy/rank-roles.js — which builders hold a rank the governance
|
|
4
|
+
// backfill cannot match? (task 1002873; moved here from
|
|
5
|
+
// scripts/bongos/audit-rank-roles.js in task 1004878 so the server's daily check and
|
|
6
|
+
// the CLI share ONE definition).
|
|
7
|
+
//
|
|
8
|
+
// Pure: no I/O, no core require. The live ladder is an ARGUMENT — the poller passes
|
|
9
|
+
// api.LIVE_RANK_LADDER, the CLI passes db-kernel's — because a module file reaches
|
|
10
|
+
// core only through the doorway.
|
|
11
|
+
|
|
12
|
+
// findRanksWithoutRoles — given roster rows and the live ladder, which builders hold
|
|
13
|
+
// a rank that `governance_002`'s INNER JOIN cannot match?
|
|
14
|
+
//
|
|
15
|
+
// A row with NO rank at all counts: NULL fails the INNER JOIN the same way a
|
|
16
|
+
// legacy value does, and reads as "fine" in every UI that renders a blank.
|
|
17
|
+
function findRanksWithoutRoles(rows, liveRanks) {
|
|
18
|
+
const live = new Set(liveRanks || []);
|
|
19
|
+
return (Array.isArray(rows) ? rows : [])
|
|
20
|
+
.filter((b) => b && !live.has(b.rank))
|
|
21
|
+
.map((b) => ({
|
|
22
|
+
id: b.id != null ? String(b.id) : null,
|
|
23
|
+
// The login is what an operator needs to act; it is already visible to any
|
|
24
|
+
// caller authorised to read the roster, so this adds no exposure.
|
|
25
|
+
github_login: b.github_login || null,
|
|
26
|
+
rank: b.rank === undefined ? null : b.rank,
|
|
27
|
+
status: b.status || null,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// summarize — the rank histogram, so a CLEAN run still says what it looked at.
|
|
32
|
+
// A green check that prints nothing is indistinguishable from one that read
|
|
33
|
+
// nothing, which is the failure mode this whole check is about.
|
|
34
|
+
function summarize(rows) {
|
|
35
|
+
const counts = new Map();
|
|
36
|
+
for (const b of Array.isArray(rows) ? rows : []) {
|
|
37
|
+
const key = b && b.rank ? b.rank : '(none)';
|
|
38
|
+
counts.set(key, (counts.get(key) || 0) + 1);
|
|
39
|
+
}
|
|
40
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ideaFor — the one idea a finding becomes, filed by either caller.
|
|
44
|
+
function ideaFor(offenders, { filedBy = 'scripts/bongos/audit-rank-roles.js' } = {}) {
|
|
45
|
+
return {
|
|
46
|
+
title: `[rank-roles] ${offenders.length} builder(s) on a rank with no governance role`,
|
|
47
|
+
bodyMd: [
|
|
48
|
+
`${offenders.length} builder(s) hold a rank with no matching governance_roles row.`,
|
|
49
|
+
'',
|
|
50
|
+
'They resolve to ZERO permissions — admitted by rank threshold under requireRank,',
|
|
51
|
+
'refused by the permission resolver, and given no reason (ADR 0151 fail-closed).',
|
|
52
|
+
'',
|
|
53
|
+
...offenders.map((o) => `- builder ${o.id}${o.github_login ? ` (${o.github_login})` : ''} rank=${o.rank === null ? '(none)' : o.rank} status=${o.status || '?'}`),
|
|
54
|
+
'',
|
|
55
|
+
'Fix: move each to a live rank through the normal rank-change path, which rotates',
|
|
56
|
+
'the governance role in the same transaction. Not auto-repaired: changing a rank is',
|
|
57
|
+
'an authority decision, and a sweep is the wrong actor for it.',
|
|
58
|
+
'',
|
|
59
|
+
`Filed by ${filedBy} (task 1002873).`,
|
|
60
|
+
].join('\n'),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { findRanksWithoutRoles, summarize, ideaFor };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/autonomy/server-schedule.js — which routines the always-on server fires
|
|
4
|
+
// itself, and how often (task 1004878).
|
|
5
|
+
//
|
|
6
|
+
// ONE TABLE, TWO READERS THAT MUST AGREE. The pollers in ./pollers/ take their
|
|
7
|
+
// periods from here, and scripts/bongos/routine-schedule.js reads the same rows to
|
|
8
|
+
// decide whether a routine's declared `cadence:` has something that fires it. Before
|
|
9
|
+
// this table the only scheduler that check could see was .github/workflows/, so a
|
|
10
|
+
// routine fired in-process (peer-vote-tally) read as "manual only", and the routines
|
|
11
|
+
// nothing fired at all read the same way — the two cases were indistinguishable.
|
|
12
|
+
//
|
|
13
|
+
// Deliberately dependency-free: the merge-gate check requires this file in a plain
|
|
14
|
+
// checkout with no database and no module loader.
|
|
15
|
+
|
|
16
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
17
|
+
const WEEK_MS = 7 * DAY_MS;
|
|
18
|
+
|
|
19
|
+
// The cadence names a server row may use, and the period each one means. A subset of
|
|
20
|
+
// routine-schedule.js's vocabulary: nothing in-process runs more often than daily.
|
|
21
|
+
const CADENCE_MS = Object.freeze({ daily: DAY_MS, weekly: WEEK_MS });
|
|
22
|
+
|
|
23
|
+
const SERVER_ROUTINES = Object.freeze([
|
|
24
|
+
{ routine: 'peer-vote-tally', cadence: 'weekly', poller: 'routine-timers' },
|
|
25
|
+
{ routine: 'rank-roles-audit', cadence: 'daily', poller: 'daily-checks' },
|
|
26
|
+
{ routine: 'dep-audit-nightly', cadence: 'daily', poller: 'daily-checks' },
|
|
27
|
+
].map(Object.freeze));
|
|
28
|
+
|
|
29
|
+
// periodMs('rank-roles-audit') → 86400000. Throws on a routine the table does not
|
|
30
|
+
// carry, so a poller can never quietly fall back to a period nobody declared.
|
|
31
|
+
function periodMs(routine) {
|
|
32
|
+
const row = SERVER_ROUTINES.find((r) => r.routine === routine);
|
|
33
|
+
if (!row) throw new Error(`server-schedule: '${routine}' is not a server-fired routine`);
|
|
34
|
+
return CADENCE_MS[row.cadence];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { SERVER_ROUTINES, CADENCE_MS, periodMs, DAY_MS, WEEK_MS };
|
|
@@ -166,7 +166,11 @@ const PERMISSIONS = [
|
|
|
166
166
|
{ key: 'dependency.cross_tier.manage', system: true, floor: 'archon', guards: 'POST|DELETE /dependencies — cross-tier structural edges (ADR 0086)' },
|
|
167
167
|
{ key: 'goal.scope.manage', system: true, floor: 'archon', guards: 'cross-tier goal-scope edges + protected-scope admission (ADR 0086)' },
|
|
168
168
|
{ key: 'government.manage', system: true, floor: 'archon', guards: 'the Government tab routes (ranks/perms/assignments CRUD, reads included — R110) — meta-escalation' },
|
|
169
|
-
|
|
169
|
+
// RETIRED (task 1004859): both gate-approval routes and the held-PR list on the /gate
|
|
170
|
+
// page were removed, because the gate-review CI check they waited on never ran. The key stays only
|
|
171
|
+
// because grant rows may still name it; dropping it from this list needs a migration
|
|
172
|
+
// that deletes those rows (the government_019 precedent, task 1003894). It guards nothing.
|
|
173
|
+
{ key: 'gate_approval.decide', system: true, floor: 'archon', guards: 'nothing since task 1004859 removed POST /gate-approvals/:pr/approve; the key is retired and awaits a grant-row cleanup' },
|
|
170
174
|
{ key: 'deploy.push_main', system: true, substrate: true, floor: 'archon', guards: 'git push origin main / the deploy — substrate-enforced (pre-push, main-audit)' },
|
|
171
175
|
{ key: 'path.protected.modify', system: true, substrate: true, floor: 'archon', guards: 'modifying a PROTECTED_GLOBS path below the Metic floor — substrate (ADR 0043)' },
|
|
172
176
|
{ key: 'infra.ssh', system: true, substrate: true, floor: 'archon', guards: 'ssh to the droplet / operator surface — substrate (credential placement)' },
|
|
@@ -305,11 +309,10 @@ const PERMISSIONS = [
|
|
|
305
309
|
{ key: 'session.search', system: false, floor: 'metic', guards: 'GET /sessions/search (ADR 0157, was archon)' },
|
|
306
310
|
{ key: 'memory.read.any', system: false, floor: 'metic', scope: 'any', resource: 'memory', verb: 'read', guards: 'GET /memory/builders/:id/file[s] — cross-builder memory read (ADR 0157, was archon)' },
|
|
307
311
|
{ key: 'audit_log.read', system: false, floor: 'metic', guards: 'GET /audit-log (ADR 0157, was archon)' },
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
{ key: 'gate_approval.read', system: false, floor: 'metic', guards: 'GET /gate-approvals — the Gate page read side (approve = gate_approval.decide, archon)' },
|
|
312
|
+
// RETIRED (task 1004859), with `gate_approval.decide` above: GET /gate-approvals and
|
|
313
|
+
// the held-PR list it fed are gone. Kept for the same reason — grant rows may name it,
|
|
314
|
+
// and removing the key needs a migration that deletes them. It guards nothing.
|
|
315
|
+
{ key: 'gate_approval.read', system: false, floor: 'metic', guards: 'nothing since task 1004859 removed GET /gate-approvals and the held-PR list it fed; the key is retired and awaits a grant-row cleanup' },
|
|
313
316
|
|
|
314
317
|
// ── ownership-scoped + baseline own-work (floor thetes / xenos) ───────────────
|
|
315
318
|
{ key: 'task.claim.any', system: false, floor: 'thetes', guards: 'claim the general queue (per-task requires_rank floor still applies)' },
|
|
@@ -391,7 +394,7 @@ const PERMISSIONS = [
|
|
|
391
394
|
// `requireGovernmentPage`) once the floor moved. Viewing is all this grants: every
|
|
392
395
|
// write those pages reach is its own atom, and `government.manage` (the
|
|
393
396
|
// ranks/permissions tab) stayed archon.
|
|
394
|
-
{ key: 'page.view.government', system: false, floor: 'metic', guards: 'the government/monitoring pages — /watch, /gate, /government (requireGovernmentPage; write = government.manage, archon)' },
|
|
397
|
+
{ key: 'page.view.government', system: false, floor: 'metic', guards: 'the government/monitoring pages — /watch, /gate (the Autobongos panel), /government (requireGovernmentPage; write = government.manage, archon)' },
|
|
395
398
|
];
|
|
396
399
|
|
|
397
400
|
// ── invariants asserted at module load (fail loud on an authoring typo) ─────────
|
|
@@ -246,7 +246,6 @@ const MATTER_ACTIONS = Object.freeze({
|
|
|
246
246
|
}),
|
|
247
247
|
release: Object.freeze({
|
|
248
248
|
approve_override: Object.freeze({ request_id: 'id' }),
|
|
249
|
-
approve_gate: Object.freeze({ pr: 'id' }),
|
|
250
249
|
}),
|
|
251
250
|
review: Object.freeze({
|
|
252
251
|
satisfy_criterion: Object.freeze({ criterion_id: 'id' }),
|
|
@@ -105,7 +105,6 @@ const AREAS = Object.freeze([
|
|
|
105
105
|
]) }),
|
|
106
106
|
Object.freeze({ key: 'release', name: 'Release', gate: 'core.pin.move',
|
|
107
107
|
today: 'whoever may put a release live', acts: Object.freeze([
|
|
108
|
-
'POST /gate-approvals/:pr/approve',
|
|
109
108
|
'POST /override-requests/:id/decide',
|
|
110
109
|
'POST /provisioning/instances/:id/core-upgrade',
|
|
111
110
|
'POST /provisioning/instances/:id/update-channel',
|
|
@@ -108,7 +108,6 @@ const EXPECTED_SOURCES = Object.freeze([
|
|
|
108
108
|
Object.freeze({ source: 'redteam_reports', building: 'disputes', label: 'red-team reports' }),
|
|
109
109
|
Object.freeze({ source: 'criteria_uat', building: 'review', label: 'criteria awaiting UAT' }),
|
|
110
110
|
Object.freeze({ source: 'core_update', building: 'release', label: 'core update' }),
|
|
111
|
-
Object.freeze({ source: 'gate_approvals', building: 'release', label: 'CI gate approvals' }),
|
|
112
111
|
Object.freeze({ source: 'autobongos_fence', building: 'automation', label: 'the Autobongos fence' }),
|
|
113
112
|
Object.freeze({ source: 'module_rechecks', building: 'release', label: 'module re-checks' }),
|
|
114
113
|
// task 1004712: a dry pot's borrow from the reserve, a treasury matter (treasury.js)
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// THE OUT-OF-BAND SUBSTRATE STAYS THE ENFORCER. Nothing here enforces
|
|
15
15
|
// anything. The enforcers are the git pre-push hook, the grader pre-pass, the
|
|
16
|
-
// post-push main audit
|
|
16
|
+
// post-push main audit and the goal-scope check — they read
|
|
17
17
|
// the registry with no DB, no server and no HTTP, and they are what actually
|
|
18
18
|
// blocks a sub-floor change (ADR 0043). Re-mapping a floor here changes what
|
|
19
19
|
// the substrate protects; it can never make the request path the authority.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"",
|
|
8
8
|
"POLICY OWNS / SUBSTRATE ENFORCES. Editing this file re-maps POLICY. It does NOT move",
|
|
9
9
|
"enforcement into the app: the pre-push git hook, the grader pre-pass, main-audit.js,",
|
|
10
|
-
"
|
|
10
|
+
"and the goal-scope check all read this registry OUT OF BAND (no DB, no",
|
|
11
11
|
"server, no HTTP) and remain the enforcers (ADR 0043). A government-tab edit can widen or",
|
|
12
12
|
"narrow the protected set and re-map a floor; it can never make the request path the",
|
|
13
13
|
"authority.",
|
|
@@ -82,13 +82,12 @@
|
|
|
82
82
|
{ "glob": "scripts/bongos/ship-visual.js", "group": "pipeline", "why": "task 1004024. The 2026-08-23 carve of ship.js (task 1003205): the land path, the ship state machine, the grade gate steps, the merge and pushVia, about 6,200 lines behind a 373-line protected entry point." },
|
|
83
83
|
{ "glob": "scripts/bongos/confirm.js", "group": "pipeline", "why": "the confirm step" },
|
|
84
84
|
{ "glob": "scripts/bongos/merge-mode.js", "group": "pipeline", "why": "the manual land fallback" },
|
|
85
|
-
{ "glob": "scripts/bongos/ci-grade.js", "group": "pipeline", "why": "CI grade-gate driver — steers the grade check (ADR 0058)" },
|
|
86
85
|
{ "glob": "scripts/bongos/grade-smoke.js", "group": "pipeline", "why": "grade-smoke driver (ADR 0058)" },
|
|
87
86
|
{ "glob": "scripts/bongos/conductor-dispatch.js", "group": "pipeline", "why": "mid-flight grader dispatch (ADR 0058)" },
|
|
88
87
|
{ "glob": "scripts/bongos/main-audit.js", "group": "pipeline", "why": "the post-push audit — the authoritative substrate layer (self-protect)" },
|
|
89
88
|
{ "glob": "scripts/bongos/push-main.js", "group": "pipeline", "why": "the sanctioned main-push (carries the override)" },
|
|
90
89
|
{ "glob": "scripts/bongos/run-unit-tests.js", "group": "pipeline", "why": "the unit-gate test SELECTOR — muting tests here blunts the CI gate (ADR 0058)" },
|
|
91
|
-
{ "glob": "scripts/bongos/
|
|
90
|
+
{ "glob": "scripts/bongos/hard-floor.js", "group": "pipeline", "why": "task 1004859. The trust-boundary hard-floor list (self-protect). It moved here from scripts/bongos/gate-review.js when that never-run CI classifier was removed; publish-manifest.js and adopt-preflight.js read it." },
|
|
92
91
|
{ "glob": "scripts/bongos/setup.js", "group": "pipeline", "why": "/builder-setup — installs the pre-push hook" },
|
|
93
92
|
{ "glob": "scripts/bongos/install-git-hooks.js", "group": "pipeline", "why": "the hook installer (self-protect)" },
|
|
94
93
|
{ "glob": "scripts/bongos/hooks/", "group": "pipeline", "why": "the git hooks themselves (self-protect)" },
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
93
93
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
94
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
94
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
95
95
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
96
96
|
<script src="/builders/palette.js" defer></script>
|
|
97
97
|
<script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
chrome exists before the page script runs. -->
|
|
54
54
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
55
55
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
56
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
56
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
57
57
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
58
58
|
<script src="/builders/palette.js" defer></script>
|
|
59
59
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
62
62
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
63
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
63
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
64
64
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
65
65
|
<script src="/builders/palette.js" defer></script>
|
|
66
66
|
<script src="/builders/dom-utils.js?v=2026-06-16-board"></script>
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
109
109
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
110
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
110
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
111
111
|
<script src="/builders/palette.js" defer></script>
|
|
112
112
|
<script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
|
|
113
113
|
<script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
|
|
151
151
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
152
152
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
153
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
153
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
154
154
|
<script src="/builders/palette.js" defer></script>
|
|
155
155
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
156
156
|
<script src="/builders/collab-lib.js?v=2026-09-24-archive"></script>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
55
55
|
<script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
|
|
56
|
-
<script src="/builders/shell.js?v=2026-10-25-
|
|
56
|
+
<script src="/builders/shell.js?v=2026-10-25-autobongos"></script>
|
|
57
57
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
58
58
|
<script src="/builders/palette.js" defer></script>
|
|
59
59
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|