@openrig/cli 0.4.3 → 0.4.4
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/daemon/assets/guidance/openrig-start.md +6 -0
- package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +49 -21
- package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +12 -12
- package/daemon/dist/build-info.d.ts +12 -0
- package/daemon/dist/build-info.d.ts.map +1 -0
- package/daemon/dist/build-info.js +11 -0
- package/daemon/dist/build-info.js.map +1 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts +18 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.d.ts.map +1 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js +22 -0
- package/daemon/dist/db/migrations/048_queue_item_evidence_ref.js.map +1 -0
- package/daemon/dist/domain/feed/attention-aggregator.d.ts +31 -0
- package/daemon/dist/domain/feed/attention-aggregator.d.ts.map +1 -0
- package/daemon/dist/domain/feed/attention-aggregator.js +119 -0
- package/daemon/dist/domain/feed/attention-aggregator.js.map +1 -0
- package/daemon/dist/domain/files/file-write-service.d.ts +11 -2
- package/daemon/dist/domain/files/file-write-service.d.ts.map +1 -1
- package/daemon/dist/domain/files/file-write-service.js +77 -0
- package/daemon/dist/domain/files/file-write-service.js.map +1 -1
- package/daemon/dist/domain/hosts/fanout-contract.d.ts +33 -0
- package/daemon/dist/domain/hosts/fanout-contract.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/fanout-contract.js +23 -0
- package/daemon/dist/domain/hosts/fanout-contract.js.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts +49 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js +165 -0
- package/daemon/dist/domain/hosts/hosts-registry-reader.js.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +35 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js +105 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts +71 -0
- package/daemon/dist/domain/human-route-enforcer.d.ts.map +1 -0
- package/daemon/dist/domain/human-route-enforcer.js +80 -0
- package/daemon/dist/domain/human-route-enforcer.js.map +1 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts +8 -0
- package/daemon/dist/domain/mission-control/audit-browse.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/audit-browse.js +19 -0
- package/daemon/dist/domain/mission-control/audit-browse.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-action-log.js +4 -0
- package/daemon/dist/domain/mission-control/mission-control-action-log.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-fleet-cli-capability.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js +1 -1
- package/daemon/dist/domain/mission-control/mission-control-read-layer.js.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts +28 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js +125 -0
- package/daemon/dist/domain/mission-control/mission-control-write-contract.js.map +1 -1
- package/daemon/dist/domain/ps-projection.d.ts +28 -1
- package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
- package/daemon/dist/domain/ps-projection.js +39 -0
- package/daemon/dist/domain/ps-projection.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +25 -1
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +111 -7
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/review/brief-spine.d.ts +19 -0
- package/daemon/dist/domain/review/brief-spine.d.ts.map +1 -0
- package/daemon/dist/domain/review/brief-spine.js +86 -0
- package/daemon/dist/domain/review/brief-spine.js.map +1 -0
- package/daemon/dist/domain/review/compose.d.ts +228 -0
- package/daemon/dist/domain/review/compose.d.ts.map +1 -0
- package/daemon/dist/domain/review/compose.js +773 -0
- package/daemon/dist/domain/review/compose.js.map +1 -0
- package/daemon/dist/domain/review/freeze.d.ts +31 -0
- package/daemon/dist/domain/review/freeze.d.ts.map +1 -0
- package/daemon/dist/domain/review/freeze.js +257 -0
- package/daemon/dist/domain/review/freeze.js.map +1 -0
- package/daemon/dist/domain/review/gather.d.ts +100 -0
- package/daemon/dist/domain/review/gather.d.ts.map +1 -0
- package/daemon/dist/domain/review/gather.js +676 -0
- package/daemon/dist/domain/review/gather.js.map +1 -0
- package/daemon/dist/domain/review/types.d.ts +258 -0
- package/daemon/dist/domain/review/types.d.ts.map +1 -0
- package/daemon/dist/domain/review/types.js +35 -0
- package/daemon/dist/domain/review/types.js.map +1 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts +64 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -0
- package/daemon/dist/domain/scope/scope-approve.js +153 -0
- package/daemon/dist/domain/scope/scope-approve.js.map +1 -0
- package/daemon/dist/domain/scope/scope-audit.d.ts +9 -1
- package/daemon/dist/domain/scope/scope-audit.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-audit.js +173 -1
- package/daemon/dist/domain/scope/scope-audit.js.map +1 -1
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts +49 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.d.ts.map +1 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js +132 -0
- package/daemon/dist/domain/topology/multi-rig-launcher.js.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts +29 -0
- package/daemon/dist/domain/topology/remote-up-leaf.d.ts.map +1 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js +60 -0
- package/daemon/dist/domain/topology/remote-up-leaf.js.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts +34 -0
- package/daemon/dist/domain/topology/topology-manifest.d.ts.map +1 -0
- package/daemon/dist/domain/topology/topology-manifest.js +195 -0
- package/daemon/dist/domain/topology/topology-manifest.js.map +1 -0
- package/daemon/dist/domain/types.d.ts +5 -0
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/up-command-router.d.ts +1 -1
- package/daemon/dist/domain/up-command-router.d.ts.map +1 -1
- package/daemon/dist/domain/up-command-router.js +34 -2
- package/daemon/dist/domain/up-command-router.js.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts +17 -0
- package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.js +109 -0
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js +51 -6
- package/daemon/dist/domain/workspace/default-workspace-scaffold.js.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts +11 -0
- package/daemon/dist/domain/workspace/workspace-doctor.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/workspace-doctor.js +82 -0
- package/daemon/dist/domain/workspace/workspace-doctor.js.map +1 -1
- package/daemon/dist/routes/config.d.ts.map +1 -1
- package/daemon/dist/routes/config.js +15 -6
- package/daemon/dist/routes/config.js.map +1 -1
- package/daemon/dist/routes/files.d.ts.map +1 -1
- package/daemon/dist/routes/files.js +45 -1
- package/daemon/dist/routes/files.js.map +1 -1
- package/daemon/dist/routes/mission-control.d.ts.map +1 -1
- package/daemon/dist/routes/mission-control.js +67 -2
- package/daemon/dist/routes/mission-control.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts +8 -2
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +44 -4
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/review.d.ts +3 -0
- package/daemon/dist/routes/review.d.ts.map +1 -0
- package/daemon/dist/routes/review.js +169 -0
- package/daemon/dist/routes/review.js.map +1 -0
- package/daemon/dist/routes/scope-approve.d.ts +3 -0
- package/daemon/dist/routes/scope-approve.d.ts.map +1 -0
- package/daemon/dist/routes/scope-approve.js +55 -0
- package/daemon/dist/routes/scope-approve.js.map +1 -0
- package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
- package/daemon/dist/routes/scope-audit.js +28 -1
- package/daemon/dist/routes/scope-audit.js.map +1 -1
- package/daemon/dist/routes/slices.d.ts.map +1 -1
- package/daemon/dist/routes/slices.js +49 -9
- package/daemon/dist/routes/slices.js.map +1 -1
- package/daemon/dist/routes/terminal-ws.d.ts.map +1 -1
- package/daemon/dist/routes/terminal-ws.js +58 -21
- package/daemon/dist/routes/terminal-ws.js.map +1 -1
- package/daemon/dist/routes/up.d.ts.map +1 -1
- package/daemon/dist/routes/up.js +84 -0
- package/daemon/dist/routes/up.js.map +1 -1
- package/daemon/dist/routes/workspace.js +2 -2
- package/daemon/dist/server.d.ts +1 -0
- package/daemon/dist/server.d.ts.map +1 -1
- package/daemon/dist/server.js +14 -1
- package/daemon/dist/server.js.map +1 -1
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +18 -3
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/docs/reference/agent-startup-guide.md +1 -1
- package/daemon/docs/reference/product-factory-vps-runbook.md +132 -0
- package/daemon/docs/reference/sdlc-conventions.md +187 -0
- package/daemon/specs/agents/conveyor/builder/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/builder/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/lead/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/lead/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/planner/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/planner/guidance/role.md +1 -0
- package/daemon/specs/agents/conveyor/reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/conveyor/reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/design/product-designer/agent.yaml +1 -1
- package/daemon/specs/agents/design/product-designer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/implementer/agent.yaml +1 -1
- package/daemon/specs/agents/development/implementer/guidance/role.md +1 -0
- package/daemon/specs/agents/development/qa/agent.yaml +1 -1
- package/daemon/specs/agents/development/qa/guidance/role.md +1 -0
- package/daemon/specs/agents/orchestration/orchestrator/agent.yaml +1 -1
- package/daemon/specs/agents/orchestration/orchestrator/guidance/role.md +1 -0
- package/daemon/specs/agents/product-management/pm/agent.yaml +1 -0
- package/daemon/specs/agents/product-management/pm/guidance/role.md +13 -0
- package/daemon/specs/agents/review/independent-reviewer/agent.yaml +1 -1
- package/daemon/specs/agents/review/independent-reviewer/guidance/role.md +1 -0
- package/daemon/specs/agents/shared/agent.yaml +2 -0
- package/daemon/specs/agents/shared/skills/core/mission-slice-sop/SKILL.md +115 -0
- package/daemon/specs/agents/shared/skills/core/openrig-operator/SKILL.md +4 -4
- package/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md +69 -23
- package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +2 -2
- package/daemon/specs/rigs/focused/pm-team/CULTURE.md +4 -0
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +1 -1
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/startup/context.md +1 -1
- package/daemon/specs/rigs/preview/product-team/CULTURE.md +4 -0
- package/dist/build-info.d.ts +8 -0
- package/dist/build-info.d.ts.map +1 -0
- package/dist/build-info.js +11 -0
- package/dist/build-info.js.map +1 -0
- package/dist/commands/config-init-workspace.d.ts.map +1 -1
- package/dist/commands/config-init-workspace.js +20 -4
- package/dist/commands/config-init-workspace.js.map +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/expand.js +1 -1
- package/dist/commands/expand.js.map +1 -1
- package/dist/commands/file.d.ts +7 -0
- package/dist/commands/file.d.ts.map +1 -0
- package/dist/commands/file.js +77 -0
- package/dist/commands/file.js.map +1 -0
- package/dist/commands/host.d.ts +34 -0
- package/dist/commands/host.d.ts.map +1 -0
- package/dist/commands/host.js +448 -0
- package/dist/commands/host.js.map +1 -0
- package/dist/commands/proof.d.ts +54 -0
- package/dist/commands/proof.d.ts.map +1 -0
- package/dist/commands/proof.js +311 -0
- package/dist/commands/proof.js.map +1 -0
- package/dist/commands/ps.d.ts +41 -7
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +343 -131
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +76 -0
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +140 -3
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/send.js +13 -4
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/up.d.ts +2 -0
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +69 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/workspace.js +6 -6
- package/dist/commands/workspace.js.map +1 -1
- package/dist/config-store.d.ts +4 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +68 -0
- package/dist/config-store.js.map +1 -1
- package/dist/cross-host-executor.d.ts +1 -0
- package/dist/cross-host-executor.d.ts.map +1 -1
- package/dist/cross-host-executor.js +4 -1
- package/dist/cross-host-executor.js.map +1 -1
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +4 -1
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/host-registry.d.ts +9 -0
- package/dist/host-registry.d.ts.map +1 -1
- package/dist/host-registry.js +32 -2
- package/dist/host-registry.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/file-transfer.d.ts +89 -0
- package/dist/lib/file-transfer.d.ts.map +1 -0
- package/dist/lib/file-transfer.js +300 -0
- package/dist/lib/file-transfer.js.map +1 -0
- package/dist/lib/hosts/fanout-contract.d.ts +33 -0
- package/dist/lib/hosts/fanout-contract.d.ts.map +1 -0
- package/dist/lib/hosts/fanout-contract.js +23 -0
- package/dist/lib/hosts/fanout-contract.js.map +1 -0
- package/dist/lib/scope/scope-audit.d.ts +9 -1
- package/dist/lib/scope/scope-audit.d.ts.map +1 -1
- package/dist/lib/scope/scope-audit.js +173 -1
- package/dist/lib/scope/scope-audit.js.map +1 -1
- package/dist/lib/scope/templates.d.ts +1 -0
- package/dist/lib/scope/templates.d.ts.map +1 -1
- package/dist/lib/scope/templates.js +9 -0
- package/dist/lib/scope/templates.js.map +1 -1
- package/dist/lib/scope-templates/backlog-deprecation.md +16 -0
- package/dist/lib/scope-templates/backlog-tech-debt.md +16 -0
- package/dist/lib/scope-templates/bug-fix.md +16 -0
- package/dist/lib/scope-templates/implementation-prd.md +33 -0
- package/dist/lib/scope-templates/mission-placeholder.md +1 -1
- package/dist/lib/scope-templates/mission-progress.md +1 -1
- package/dist/lib/scope-templates/mission-release.md +1 -1
- package/dist/lib/scope-templates/placeholder.md +11 -7
- package/dist/lib/scope-templates/proof.md +5 -1
- package/dist/lib/scope-templates/release-feature.md +14 -6
- package/dist/lib/scope-templates/research.md +16 -0
- package/dist/lib/scope-templates/slice-progress.md +1 -1
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +11 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/index-D69ZhNIr.js +598 -0
- package/ui/dist/assets/index-DJYun-8d.css +32 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DYZniQcc.js +0 -597
- package/ui/dist/assets/index-trcb4Yf_.css +0 -32
|
@@ -17,6 +17,12 @@ That skill covers the broader surface, including chatroom, discovery, adopt/bind
|
|
|
17
17
|
Your per-session startup guidance may also name additional packaged skills for your role and pod.
|
|
18
18
|
Load those too. This shared overlay is only the common bootstrap floor, not the full operating manual for your seat.
|
|
19
19
|
|
|
20
|
+
## Working missions and slices (the SDLC)
|
|
21
|
+
|
|
22
|
+
If your seat works missions or slices, load the packaged `mission-slice-sop` skill BEFORE authoring or building — it teaches the flow the Living Notes UI projects: intent → mini-requirements + proof contract (→ mockups for UI slices) → plan-lock (`rig scope slice approve --scope spec`) → build the locked set → QA visual compare → `rig proof add … --media` drops (the C1 drop verb — never hand-place proof files) → proof-lock (`--scope delivery`).
|
|
23
|
+
|
|
24
|
+
The conventions themselves (section names, proof-contract format, the two locks, C1 proof headers) live in ONE shipped document: `docs/reference/sdlc-conventions.md` (in the CLI package). `rig scope slice create` scaffolds the convention sections for every template kind; `rig scope audit` is the advisory backstop — it records and advises, it never blocks your work.
|
|
25
|
+
|
|
20
26
|
## Identity
|
|
21
27
|
|
|
22
28
|
Run this first after launch or compaction to recover your identity:
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mission-slice-sop
|
|
3
|
-
description: Use when working a mission or slice — how to track, prove, hand off, and carry state across agents and compaction via the canonical files (PROGRESS.md / PROOF.md / MISSION_NOTES.md / MISSION_BRIEF.md / README.md). Covers the per-file WHO/WHEN/HOW rules, the four-legs lifecycle
|
|
3
|
+
description: Use when working a mission or slice — the full SDLC flow (intent → mini-requirements + proof contract → mockups → plan-lock → build → QA visual compare → `rig proof` drops → proof-lock) plus how to track, prove, hand off, and carry state across agents and compaction via the canonical files (PROGRESS.md / PROOF.md / MISSION_NOTES.md / MISSION_BRIEF.md / README.md / IMPLEMENTATION-PRD.md). Covers the convention sections the Living Notes UI projects, the two locks, the three role contracts, per-file WHO/WHEN/HOW rules, the four-legs lifecycle, hot-potato queue handoffs, the moment-of-truth checklist, the deterministic `rig scope audit` backstop, and the ghost-text capture gotcha.
|
|
4
4
|
metadata:
|
|
5
5
|
openrig:
|
|
6
6
|
stage: shipped
|
|
7
|
-
last_verified: "2026-07-
|
|
7
|
+
last_verified: "2026-07-06"
|
|
8
8
|
distribution_scope: product-bound
|
|
9
9
|
source_evidence: |
|
|
10
|
-
Authored + embedded in release-0.4.3 slice 32 (mission-slice-sop)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
Authored + embedded in release-0.4.3 slice 32 (mission-slice-sop);
|
|
11
|
+
moved into the canonical product skill source and updated in
|
|
12
|
+
release-0.4.4 slice 23 (operationalize the SDLC control plane) to
|
|
13
|
+
teach the full SDLC flow the Living Notes UI projects: the convention
|
|
14
|
+
sections, the proof-contract pairing, the two staged-approval locks,
|
|
15
|
+
C1 proof drops, and the three role contracts. Conventions SSOT:
|
|
16
|
+
docs/reference/sdlc-conventions.md (shipped with the CLI package).
|
|
17
|
+
The deterministic backstop is the `rig scope audit` classifier.
|
|
17
18
|
sibling_skills:
|
|
18
19
|
- queue-handoff
|
|
19
20
|
- seat-continuity-and-handover
|
|
@@ -22,17 +23,43 @@ metadata:
|
|
|
22
23
|
|
|
23
24
|
# Mission/Slice SOP — how you work a mission & slice
|
|
24
25
|
|
|
25
|
-
Use this skill to actually **do** mission/slice work the way OpenRig expects: track on the canonical files, prove on them, hand off through them, and survive compaction on them. **Do the work described here; do not merely explain the protocol.** The deterministic backstop is `rig scope audit` —
|
|
26
|
+
Use this skill to actually **do** mission/slice work the way OpenRig expects: author the convention sections, track on the canonical files, prove on them, hand off through them, and survive compaction on them. **Do the work described here; do not merely explain the protocol.** The deterministic backstop is `rig scope audit` — the audit classifier is the source of truth for adherence. The conventions themselves live in ONE place: **`docs/reference/sdlc-conventions.md`** (shipped with the CLI package) — this skill teaches the flow; the SSOT defines the formats.
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
## The SDLC flow (intent → proof)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
intent → mini-requirements + proof contract → (UI slices: mockups)
|
|
32
|
+
→ plan-lock (rig scope slice approve --scope spec)
|
|
33
|
+
→ build the LOCKED set
|
|
34
|
+
→ QA: mockup ↔ delivered VISUAL compare
|
|
35
|
+
→ proof drops (rig proof add <slice> …)
|
|
36
|
+
→ proof-lock (rig scope slice approve --scope delivery)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
1. **Record intent** verbatim in the slice's `## Intent` section (`rig scope slice create` scaffolds it — every template kind).
|
|
40
|
+
2. **Author the mini-requirements + proof contract**: `## Mini-requirements` is the concise one-glance tier (approval starts there); `## Proof contract` is a checkbox list of promised deliverables, each written as an observable outcome. UI deliverables name their planned mockup. The IMPLEMENTATION-PRD opens with the mini-requirements; everything between intent and proof is **elastic** — for a small slice the mini-requirements may BE the whole PRD.
|
|
41
|
+
3. **Plan-lock**: `rig scope slice approve <slice> --scope spec` — "the PRD matches the intent; THIS artifact set is what gets built." One daemon-side write: frontmatter stamp + append-only audit row.
|
|
42
|
+
4. **Build the locked set** — look at the mockups, not just the spec text.
|
|
43
|
+
5. **QA visual compare**: for each deliverable, load the planned mockup, produce the real artifact in a test/demo environment, visually compare, and record the verdict.
|
|
44
|
+
6. **Drop proof**: `rig proof add <slice> --artifact-type qa --verdict PASS --candidate-sha <tip> --money-evidence "…" --evidences "1,3" --media "walk.webm,panel.png" --self-check "…"` — the C1 header's closed sets validate at drop time; `--evidences` joins the drop to its proof-contract items and `--media` names the curated proof/-relative media the drop stands behind (that pairing + media set is what the UI's DELIVERED section renders).
|
|
45
|
+
7. **Proof-lock**: `rig scope slice approve <slice> --scope delivery` — the terminal sign-off. Approval is freeze/sign-off, **never** proven-green: proven-green requires the recorded C1 verdicts.
|
|
46
|
+
|
|
47
|
+
## The three role contracts
|
|
48
|
+
|
|
49
|
+
- **Planning agent:** authors intent + mini-requirements + the proof contract; produces mockups for UI deliverables and attaches them to the locked set (a UI slice with no mockup is an incomplete plan; non-UI slices have none — not a gate); locks the plan.
|
|
50
|
+
- **Build agent:** builds against the LOCKED set only; looks at the mockups.
|
|
51
|
+
- **QA agent (owns the compare):** visually compares planned vs delivered per deliverable, records verdict + note via proof drops, and **curates** the canonical proof set (bounded, mapped to deliverables; the fix-loop pile stays in `proof/`, one drill-in down). On mismatch: fix-and-re-review or kick back with the reason — never escalate a raw mismatch to the human.
|
|
28
52
|
|
|
29
53
|
## The canonical files (the operating surface)
|
|
30
54
|
|
|
31
|
-
|
|
55
|
+
> The canonical files below are the **operating surface of the work** — you track on them, prove on them, hand off through them, and survive compaction on them. They are NOT side-artifacts to "maintain"; keeping them current *is* the work.
|
|
56
|
+
|
|
57
|
+
- **README.md** (mission + slice) — the overview, OPENING with the convention sections (`## Intent` / `## Mini-requirements` / `## Proof contract`). The **mission README carries this SOP at its bottom.**
|
|
58
|
+
- **IMPLEMENTATION-PRD.md** — the full PRD; opens with the mini-requirements; the `## Proof contract` here is what the UI's DELIVERED pairing joins proof against.
|
|
32
59
|
- **PROGRESS.md** — the live delivery state. One line per outcome; links down for detail.
|
|
33
60
|
- **MISSION_NOTES.md** — the accruing handoff + tribal-knowledge doc. Blank-slate onboarding + **compaction-restore** read it. `§1` top-of-mind + per-seat `§A–§X`.
|
|
34
61
|
- **MISSION_BRIEF.md** — the steering doc (the UI "Steering" tab). The 7-section schema.
|
|
35
|
-
- **PROOF.md** (+ `proof/`) — acceptance evidence. **A slice is not done until every
|
|
62
|
+
- **PROOF.md** (+ `proof/`) — acceptance evidence. **A slice is not done until every proof-contract item has evidence.**
|
|
36
63
|
|
|
37
64
|
## Per-file rules — WHO / WHEN / HOW
|
|
38
65
|
|
|
@@ -41,10 +68,10 @@ Use this skill to actually **do** mission/slice work the way OpenRig expects: tr
|
|
|
41
68
|
- **WHEN:** after every slice-done **AND every commit**; on any material state change.
|
|
42
69
|
- **HOW:** one line per outcome (checkbox), link down for detail (workstream-continuity format); keep frontmatter `stage`/`verified` honest.
|
|
43
70
|
|
|
44
|
-
### PROOF.md
|
|
71
|
+
### PROOF.md + proof/
|
|
45
72
|
- **WHO:** the impl/QA pair that worked the slice.
|
|
46
|
-
- **WHEN:** a slice is **NOT "done"** until
|
|
47
|
-
- **HOW:** proof maps **1:1 to the
|
|
73
|
+
- **WHEN:** a slice is **NOT "done"** until every proof-contract item has evidence.
|
|
74
|
+
- **HOW:** proof maps **1:1 to the proof contract's deliverables** — put media under `proof/`, then ATTACH it with `rig proof add <slice> … --evidences <item> --media <files>` (the drop writes the C1 header the DELIVERED pairing joins on) + a line in PROOF.md stating what it proves. **Hand-placing files in `proof/` without a drop is the anti-pattern** — the deliverable stays unpaired and `unverified`. No proof → not done.
|
|
48
75
|
|
|
49
76
|
### MISSION_NOTES.md
|
|
50
77
|
- **WHO:** any agent updates `§1` (top-of-mind); each seat owns + appends to its own `§A–§X` section.
|
|
@@ -59,11 +86,11 @@ Use this skill to actually **do** mission/slice work the way OpenRig expects: tr
|
|
|
59
86
|
### README.md
|
|
60
87
|
- **WHO:** the author at creation; refreshed on rescope.
|
|
61
88
|
- **WHEN:** at mission/slice creation + when scope/theme changes.
|
|
62
|
-
- **HOW:** overview + honest frontmatter (`id`/`stage`/`verified`); the mission README carries this SOP at its bottom.
|
|
89
|
+
- **HOW:** overview + honest frontmatter (`id`/`stage`/`verified`) + the convention sections up top; the mission README carries this SOP at its bottom.
|
|
63
90
|
|
|
64
91
|
## The lifecycle (4 legs)
|
|
65
92
|
|
|
66
|
-
**SCAFFOLD** (`rig scope` creates the files from templates) → **POPULATE** (agents fill them as work happens, per the rules above) → **PROJECT** (the
|
|
93
|
+
**SCAFFOLD** (`rig scope` creates the files from templates — every slice kind emits the convention sections + `proof/` + the IMPLEMENTATION-PRD skeleton) → **POPULATE** (agents fill them as work happens, per the rules above) → **PROJECT** (the Living Notes UI reads them into INTENT → PLAN → DELIVERED) → **VERIFY** (`rig scope audit` checks adherence). "Loose freeform write + deterministic verify."
|
|
67
94
|
|
|
68
95
|
## Hot-potato (handoffs)
|
|
69
96
|
|
|
@@ -71,7 +98,7 @@ End every turn by passing the ball — a `rig queue` handoff to the next agent (
|
|
|
71
98
|
|
|
72
99
|
## Verify (deterministic backstop)
|
|
73
100
|
|
|
74
|
-
Run `rig scope audit` at slice-close.
|
|
101
|
+
Run `rig scope audit` at slice-close. It flags: committed-without-touching-PROGRESS; slice-marked-done-without-PROOF; active-mission-without-MISSION_NOTES; MISSION_BRIEF off-schema; proof artifacts violating the C1 header; missing IMPLEMENTATION-PRD on a building slice; missing convention sections (`## Intent`, a well-formed `## Proof contract`, the UI-slice mockup ref). Every convention check is **advisory / fail-open** — it records and advises, never blocks a write. **Fix the flag, don't suppress it.**
|
|
75
102
|
|
|
76
103
|
## Reading terminal captures — KNOWN GOTCHA: ghost-text autocomplete is NOT real
|
|
77
104
|
|
|
@@ -81,7 +108,8 @@ When you `rig capture` a pane, **greyed / ghost autocomplete suggestions are NOT
|
|
|
81
108
|
|
|
82
109
|
## Moment-of-truth checklist
|
|
83
110
|
|
|
84
|
-
- **
|
|
111
|
+
- **Starting a slice?** → intent recorded verbatim? mini-requirements + proof contract authored? mockups attached (UI slices)? plan locked (`--scope spec`)?
|
|
112
|
+
- **Finishing a slice?** → every proof-contract item has curated evidence via `rig proof add … --evidences --media` (C1 drops — never only hand-placed files)? PROGRESS updated? MISSION_NOTES `§1` refreshed? proof locked (`--scope delivery`)? Handed off via queue?
|
|
85
113
|
- **Committing?** → PROGRESS updated?
|
|
86
114
|
- **Compacting?** → filed your state in MISSION_NOTES?
|
|
87
|
-
- **Starting on a mission?** → read the mission README (incl. this SOP) + MISSION_NOTES
|
|
115
|
+
- **Starting on a mission?** → read the mission README (incl. this SOP) + MISSION_NOTES + `docs/reference/sdlc-conventions.md`?
|
|
@@ -61,7 +61,7 @@ Default posture on this host now:
|
|
|
61
61
|
|
|
62
62
|
Most work in OpenRig reduces to this loop:
|
|
63
63
|
- recover identity: `rig whoami --json`
|
|
64
|
-
- inspect inventory: `rig ps --nodes --json`
|
|
64
|
+
- inspect inventory: `rig ps` (fleet map) / `rig ps --nodes -A --json` (fleet nodes)
|
|
65
65
|
- read context: `rig transcript ...`, `rig ask ...`, `rig chatroom history ...`
|
|
66
66
|
- act: `rig send`, `rig capture`, `rig broadcast`, lifecycle commands
|
|
67
67
|
|
|
@@ -90,13 +90,13 @@ If the daemon is unreachable but identity can still be inferred, `--json` may re
|
|
|
90
90
|
## Inventory and Monitoring
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
-
rig ps
|
|
94
|
-
rig ps --json
|
|
95
|
-
rig ps --nodes
|
|
96
|
-
rig ps --nodes --json
|
|
93
|
+
rig ps # ALL active rigs, one compact row each (v0.4.4 default)
|
|
94
|
+
rig ps --json # bare array, all non-archived rigs
|
|
95
|
+
rig ps --nodes # current rig's nodes (session default, local only)
|
|
96
|
+
rig ps --nodes -A --json # fleet node inventory (explicit)
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
Use `rig ps --nodes --json` for the current node inventory across rigs. It is the best machine-readable operator surface for:
|
|
99
|
+
Use `rig ps --nodes -A --json` for the current node inventory across rigs (v0.4.4: `--nodes` needs an explicit scope outside a managed session). It is the best machine-readable operator surface for:
|
|
100
100
|
- session name
|
|
101
101
|
- runtime
|
|
102
102
|
- session/startup status
|
|
@@ -386,7 +386,7 @@ rig whoami --json
|
|
|
386
386
|
|
|
387
387
|
Notes:
|
|
388
388
|
- for tmux-backed self-attach, `rig whoami --json` is the right verification
|
|
389
|
-
- for raw/external self-attach, `rig ps --nodes --json` is currently the more reliable verification surface
|
|
389
|
+
- for raw/external self-attach, `rig ps --nodes -A --json` is currently the more reliable verification surface
|
|
390
390
|
- if the current shell is outside tmux, pass `--display-name <name>` when you want a stable human session label recorded
|
|
391
391
|
|
|
392
392
|
### Adopt a topology and bind live sessions
|
|
@@ -453,13 +453,13 @@ Verification loop:
|
|
|
453
453
|
```bash
|
|
454
454
|
rig discover --json
|
|
455
455
|
rig adopt <fragment.yaml> --bindings-file <bindings.yaml> --target-rig <rigId>
|
|
456
|
-
rig ps --nodes --json
|
|
456
|
+
rig ps --nodes -A --json
|
|
457
457
|
rig export <rigId> -o rig.yaml
|
|
458
458
|
```
|
|
459
459
|
|
|
460
460
|
Success looks like:
|
|
461
461
|
- the new sessions stop appearing in `rig discover`
|
|
462
|
-
- the new logical IDs appear in `rig ps --nodes --json`
|
|
462
|
+
- the new logical IDs appear in `rig ps --nodes -A --json`
|
|
463
463
|
- `rig export` includes the new pod
|
|
464
464
|
|
|
465
465
|
### Mixed-origin rigs are allowed
|
|
@@ -477,7 +477,7 @@ Current safety rule:
|
|
|
477
477
|
The proven operator pattern is:
|
|
478
478
|
- keep one OpenRig manager session outside the rig it manages
|
|
479
479
|
- address the target by rig name, not cached rig ID
|
|
480
|
-
- resolve the current owner from fresh `rig ps --nodes --json`
|
|
480
|
+
- resolve the current owner from fresh `rig ps --nodes -A --json`
|
|
481
481
|
- send the manager the spec path, bindings path, and verification steps with `rig send`
|
|
482
482
|
|
|
483
483
|
This lets ordinary agents ask the manager for OpenRig help instead of every agent needing to be an OpenRig expert.
|
|
@@ -548,7 +548,7 @@ When the CLI behaves strangely, use the smallest truthful check first:
|
|
|
548
548
|
```bash
|
|
549
549
|
rig whoami --json
|
|
550
550
|
rig daemon status
|
|
551
|
-
rig ps --nodes --json
|
|
551
|
+
rig ps --nodes -A --json
|
|
552
552
|
```
|
|
553
553
|
|
|
554
554
|
Specific operator rules:
|
|
@@ -584,7 +584,7 @@ Design assumptions that hold in the shipped CLI:
|
|
|
584
584
|
|
|
585
585
|
1. `rig whoami --json`
|
|
586
586
|
2. `rig transcript <your-session> --tail 100`
|
|
587
|
-
3. `rig ps --nodes --json`
|
|
587
|
+
3. `rig ps --nodes -A --json`
|
|
588
588
|
4. `rig chatroom history <rig> --limit 50`
|
|
589
589
|
|
|
590
590
|
## Commands That Do Not Exist
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface BuildInfo {
|
|
2
|
+
semver: string | null;
|
|
3
|
+
commit: string | null;
|
|
4
|
+
dirty: boolean | null;
|
|
5
|
+
builtAt: string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare const BUILD_INFO: BuildInfo;
|
|
8
|
+
/** The additive /healthz fields: the four stamp keys when stamped, an EMPTY
|
|
9
|
+
* object when dev — existing consumers (including exact-body probes against
|
|
10
|
+
* dev daemons) see no change. */
|
|
11
|
+
export declare function stampFields(info?: BuildInfo): Record<string, unknown>;
|
|
12
|
+
//# sourceMappingURL=build-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-info.d.ts","sourceRoot":"","sources":["../src/build-info.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,eAAO,MAAM,UAAU,EAAE,SAKxB,CAAC;AAEF;;kCAEkC;AAClC,wBAAgB,WAAW,CAAC,IAAI,GAAE,SAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGjF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// GENERATED by scripts/build-package.sh — the packaged build-identity stamp.
|
|
2
|
+
export const BUILD_INFO = {
|
|
3
|
+
semver: "0.4.4",
|
|
4
|
+
commit: "ac75777a3cad318592dae9d616a09d824f1398e5",
|
|
5
|
+
dirty: false,
|
|
6
|
+
builtAt: "2026-07-06T08:21:05Z",
|
|
7
|
+
};
|
|
8
|
+
export function stampFields(info = BUILD_INFO) {
|
|
9
|
+
if (!info.commit) return {};
|
|
10
|
+
return { semver: info.semver, commit: info.commit, dirty: info.dirty, builtAt: info.builtAt };
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-info.js","sourceRoot":"","sources":["../src/build-info.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,4EAA4E;AAC5E,+DAA+D;AAC/D,wEAAwE;AACxE,iEAAiE;AASjE,MAAM,CAAC,MAAM,UAAU,GAAc;IACnC,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,IAAI;CACd,CAAC;AAEF;;kCAEkC;AAClC,MAAM,UAAU,WAAW,CAAC,OAAkB,UAAU;IACtD,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;AAChG,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Migration } from "../migrate.js";
|
|
2
|
+
/**
|
|
3
|
+
* OPR.0.4.4.19 FR-5 — queue_items.evidence_ref column.
|
|
4
|
+
*
|
|
5
|
+
* Adds `evidence_ref TEXT` (NULLABLE) to queue_items: a pointer to the
|
|
6
|
+
* durable artifact a human judges when the item is human-routed (convention
|
|
7
|
+
* C3), authored via `--evidence-ref` on `rig queue create` / `handoff` and
|
|
8
|
+
* persistable at park time (`rig queue block`, FR-6). Follows the 044
|
|
9
|
+
* summary-column pattern exactly: additive, nullable, defensive
|
|
10
|
+
* detectQueueColumn read so pre-048 fixtures degrade.
|
|
11
|
+
*
|
|
12
|
+
* NULLABLE by contract: required ONLY for human-routed items (the §5
|
|
13
|
+
* predicate, enforced at the domain write path — FR-4/FR-5), so ordinary
|
|
14
|
+
* agent-to-agent rows stay NULL forever (BR-1 zero-friction). Do NOT make
|
|
15
|
+
* this NOT NULL or add a default.
|
|
16
|
+
*/
|
|
17
|
+
export declare const queueItemEvidenceRefSchema: Migration;
|
|
18
|
+
//# sourceMappingURL=048_queue_item_evidence_ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"048_queue_item_evidence_ref.d.ts","sourceRoot":"","sources":["../../../src/db/migrations/048_queue_item_evidence_ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAKxC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPR.0.4.4.19 FR-5 — queue_items.evidence_ref column.
|
|
3
|
+
*
|
|
4
|
+
* Adds `evidence_ref TEXT` (NULLABLE) to queue_items: a pointer to the
|
|
5
|
+
* durable artifact a human judges when the item is human-routed (convention
|
|
6
|
+
* C3), authored via `--evidence-ref` on `rig queue create` / `handoff` and
|
|
7
|
+
* persistable at park time (`rig queue block`, FR-6). Follows the 044
|
|
8
|
+
* summary-column pattern exactly: additive, nullable, defensive
|
|
9
|
+
* detectQueueColumn read so pre-048 fixtures degrade.
|
|
10
|
+
*
|
|
11
|
+
* NULLABLE by contract: required ONLY for human-routed items (the §5
|
|
12
|
+
* predicate, enforced at the domain write path — FR-4/FR-5), so ordinary
|
|
13
|
+
* agent-to-agent rows stay NULL forever (BR-1 zero-friction). Do NOT make
|
|
14
|
+
* this NOT NULL or add a default.
|
|
15
|
+
*/
|
|
16
|
+
export const queueItemEvidenceRefSchema = {
|
|
17
|
+
name: "048_queue_item_evidence_ref.sql",
|
|
18
|
+
sql: `
|
|
19
|
+
ALTER TABLE queue_items ADD COLUMN evidence_ref TEXT;
|
|
20
|
+
`,
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=048_queue_item_evidence_ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"048_queue_item_evidence_ref.js","sourceRoot":"","sources":["../../../src/db/migrations/048_queue_item_evidence_ref.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAc;IACnD,IAAI,EAAE,iCAAiC;IACvC,GAAG,EAAE;;GAEJ;CACF,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AggregatedPayload } from "../hosts/fanout-contract.js";
|
|
2
|
+
import type { HostRegistryLoadResult } from "../hosts/hosts-registry-reader.js";
|
|
3
|
+
/** The aggregation READ deadline class (per-host bound). Distinct from the
|
|
4
|
+
* up-leaf's long-running budget by design — named here, passed explicitly. */
|
|
5
|
+
export declare const ATTENTION_READ_TIMEOUT_MS = 5000;
|
|
6
|
+
/** Fixed fan-out cap — a poll across a handful of hosts; adaptive
|
|
7
|
+
* throttling is out of scope (same v1 posture as the topology walker). */
|
|
8
|
+
export declare const ATTENTION_FANOUT_CONCURRENCY = 4;
|
|
9
|
+
export type AttentionItem = Record<string, unknown>;
|
|
10
|
+
export interface AttentionAggregatorDeps {
|
|
11
|
+
/** The SAME query the shipped /api/queue/list?attention=1 route runs. */
|
|
12
|
+
listLocalAttention: () => AttentionItem[];
|
|
13
|
+
/** settings-store.listFeedHostSubscriptions (the G15-P1 dynamic class). */
|
|
14
|
+
listSubscriptions: () => Array<{
|
|
15
|
+
hostId: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
/** S11's shared reader — called lazily, only when a remote sub exists. */
|
|
19
|
+
loadRegistry: () => HostRegistryLoadResult;
|
|
20
|
+
fetchImpl?: typeof fetch;
|
|
21
|
+
env?: Record<string, string | undefined>;
|
|
22
|
+
readFile?: (path: string) => string;
|
|
23
|
+
/** Test override; production uses ATTENTION_READ_TIMEOUT_MS. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
concurrency?: number;
|
|
26
|
+
}
|
|
27
|
+
/** One merged attention payload: local ALWAYS included (stamped with the
|
|
28
|
+
* contract's LOCAL_HOST_ID), each enabled remote host fanned out under the
|
|
29
|
+
* read deadline + fixed cap, per-host status complete by construction. */
|
|
30
|
+
export declare function aggregateAttention(deps: AttentionAggregatorDeps): Promise<AggregatedPayload<AttentionItem>>;
|
|
31
|
+
//# sourceMappingURL=attention-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-aggregator.d.ts","sourceRoot":"","sources":["../../../src/domain/feed/attention-aggregator.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,6BAA6B,CAAC;AAEpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAIhF;+EAC+E;AAC/E,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAE/C;2EAC2E;AAC3E,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,aAAa,EAAE,CAAC;IAC1C,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACrE,0EAA0E;IAC1E,YAAY,EAAE,MAAM,sBAAsB,CAAC;IAC3C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgED;;2EAE2E;AAC3E,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAgCjH"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// OPR.0.4.4.15 FR-1 — daemon-side attention aggregation (the SEE architecture,
|
|
2
|
+
// call A): the LOCAL daemon fans out server-side to each subscribed host's
|
|
3
|
+
// shipped GET /api/queue/list?attention=1 over the registered HTTP+bearer
|
|
4
|
+
// transport, merges, and returns ONE AggregatedPayload. Bearer tokens never
|
|
5
|
+
// reach the browser — this module is the only place remote hosts are
|
|
6
|
+
// contacted for the feed.
|
|
7
|
+
//
|
|
8
|
+
// Reuse, not reimplementation: the LOCAL leg invokes the SAME
|
|
9
|
+
// QueueRepository.listAttention query the existing route uses (injected at
|
|
10
|
+
// the route wiring); the registry is read through S11's shared
|
|
11
|
+
// hosts-registry-reader (interface cell — never re-parsed); the remote hop
|
|
12
|
+
// rides the shared remote-daemon-http core with the READ deadline class
|
|
13
|
+
// named EXPLICITLY at this call-site (arch sharpening: 5s — a poll, not a
|
|
14
|
+
// bootstrap; the up-leaf's 120s budget lives at ITS call-site).
|
|
15
|
+
//
|
|
16
|
+
// FR-1/R15-2 honesty: EVERY subscribed host appears in hosts[] on EVERY
|
|
17
|
+
// call — ok, unreachable, auth-failed, or unsupported-transport — never
|
|
18
|
+
// all-or-nothing, never a silently thinner feed. The registry is read ONLY
|
|
19
|
+
// when ≥1 remote subscription is enabled (zero-config never touches it).
|
|
20
|
+
import { LOCAL_HOST_ID } from "../hosts/fanout-contract.js";
|
|
21
|
+
import { resolveHost } from "../hosts/hosts-registry-reader.js";
|
|
22
|
+
import { remoteJsonRequest } from "../hosts/remote-daemon-http.js";
|
|
23
|
+
/** The aggregation READ deadline class (per-host bound). Distinct from the
|
|
24
|
+
* up-leaf's long-running budget by design — named here, passed explicitly. */
|
|
25
|
+
export const ATTENTION_READ_TIMEOUT_MS = 5_000;
|
|
26
|
+
/** Fixed fan-out cap — a poll across a handful of hosts; adaptive
|
|
27
|
+
* throttling is out of scope (same v1 posture as the topology walker). */
|
|
28
|
+
export const ATTENTION_FANOUT_CONCURRENCY = 4;
|
|
29
|
+
async function readHostAttention(hostId, reg, deps) {
|
|
30
|
+
if (!reg.ok) {
|
|
31
|
+
// Registry unreadable: every subscribed host reports it honestly —
|
|
32
|
+
// the operator sees ONE actionable error per host row, local items
|
|
33
|
+
// are untouched.
|
|
34
|
+
return { status: { hostId, status: "unreachable", error: reg.error }, items: [] };
|
|
35
|
+
}
|
|
36
|
+
const resolved = resolveHost(reg.registry, hostId);
|
|
37
|
+
if (!resolved.ok) {
|
|
38
|
+
return { status: { hostId, status: "unreachable", error: resolved.error }, items: [] };
|
|
39
|
+
}
|
|
40
|
+
if (resolved.host.transport !== "http") {
|
|
41
|
+
// R15-2 (the whoami --all-hosts precedent): SSH-declared hosts are a
|
|
42
|
+
// structured per-host status, never a silently thinner feed.
|
|
43
|
+
return {
|
|
44
|
+
status: { hostId, status: "unsupported-transport", error: `host '${hostId}' is SSH-declared; the aggregated feed read requires an http-transport registry entry (url + bearer)` },
|
|
45
|
+
items: [],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const res = await remoteJsonRequest(resolved.host, "/api/queue/list?attention=1", {
|
|
49
|
+
method: "GET",
|
|
50
|
+
timeoutMs: deps.timeoutMs ?? ATTENTION_READ_TIMEOUT_MS,
|
|
51
|
+
fetchImpl: deps.fetchImpl,
|
|
52
|
+
env: deps.env,
|
|
53
|
+
readFile: deps.readFile,
|
|
54
|
+
});
|
|
55
|
+
if (res.ok) {
|
|
56
|
+
const arr = Array.isArray(res.payload) ? res.payload : [];
|
|
57
|
+
return { status: { hostId, status: "ok" }, items: arr.map((i) => ({ ...i, hostId })) };
|
|
58
|
+
}
|
|
59
|
+
switch (res.kind) {
|
|
60
|
+
case "bearer":
|
|
61
|
+
return { status: { hostId, status: "auth-failed", error: res.detail, failedStep: "permission-gate" }, items: [] };
|
|
62
|
+
case "http":
|
|
63
|
+
if (res.status === 401 || res.status === 403) {
|
|
64
|
+
return { status: { hostId, status: "auth-failed", error: `HTTP ${res.status}${res.detail ? `: ${res.detail}` : ""}`, failedStep: "permission-gate" }, items: [] };
|
|
65
|
+
}
|
|
66
|
+
// A remote 4xx/5xx on the read: the feed cannot show that host's
|
|
67
|
+
// items — the closest closed-enum truth is unreachable, with the
|
|
68
|
+
// remote's own status/text as the honest detail and the shipped
|
|
69
|
+
// FailedStep vocabulary riding as the additive detail field.
|
|
70
|
+
return { status: { hostId, status: "unreachable", error: `HTTP ${res.status}${res.detail ? `: ${res.detail}` : ""}`, failedStep: "remote-command-failed" }, items: [] };
|
|
71
|
+
case "timeout":
|
|
72
|
+
return {
|
|
73
|
+
status: {
|
|
74
|
+
hostId,
|
|
75
|
+
status: "unreachable",
|
|
76
|
+
error: res.phase === "body" ? `read timed out: response headers arrived (HTTP ${res.status}) but the body never completed` : `read timed out after ${deps.timeoutMs ?? ATTENTION_READ_TIMEOUT_MS}ms`,
|
|
77
|
+
failedStep: "remote-daemon-unreachable",
|
|
78
|
+
},
|
|
79
|
+
items: [],
|
|
80
|
+
};
|
|
81
|
+
case "network":
|
|
82
|
+
return { status: { hostId, status: "unreachable", error: res.detail, failedStep: "remote-daemon-unreachable" }, items: [] };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** One merged attention payload: local ALWAYS included (stamped with the
|
|
86
|
+
* contract's LOCAL_HOST_ID), each enabled remote host fanned out under the
|
|
87
|
+
* read deadline + fixed cap, per-host status complete by construction. */
|
|
88
|
+
export async function aggregateAttention(deps) {
|
|
89
|
+
const localItems = deps.listLocalAttention().map((i) => ({ ...i, hostId: LOCAL_HOST_ID }));
|
|
90
|
+
const hosts = [{ hostId: LOCAL_HOST_ID, status: "ok" }];
|
|
91
|
+
const items = [...localItems];
|
|
92
|
+
const subs = deps.listSubscriptions().filter((s) => s.enabled);
|
|
93
|
+
if (subs.length === 0) {
|
|
94
|
+
// Zero remote subscriptions: the registry is NEVER read; the payload
|
|
95
|
+
// is today's local feed plus the local status row.
|
|
96
|
+
return { items, hosts };
|
|
97
|
+
}
|
|
98
|
+
const reg = deps.loadRegistry();
|
|
99
|
+
const outcomes = new Array(subs.length);
|
|
100
|
+
let next = 0;
|
|
101
|
+
const cap = Math.max(1, Math.min(deps.concurrency ?? ATTENTION_FANOUT_CONCURRENCY, subs.length));
|
|
102
|
+
const workers = Array.from({ length: cap }, async () => {
|
|
103
|
+
while (true) {
|
|
104
|
+
const i = next;
|
|
105
|
+
if (i >= subs.length)
|
|
106
|
+
return;
|
|
107
|
+
next += 1;
|
|
108
|
+
outcomes[i] = await readHostAttention(subs[i].hostId, reg, deps);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
await Promise.all(workers);
|
|
112
|
+
// Subscription order preserved in both arrays (deterministic payload).
|
|
113
|
+
for (const outcome of outcomes) {
|
|
114
|
+
hosts.push(outcome.status);
|
|
115
|
+
items.push(...outcome.items);
|
|
116
|
+
}
|
|
117
|
+
return { items, hosts };
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=attention-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-aggregator.js","sourceRoot":"","sources":["../../../src/domain/feed/attention-aggregator.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,qEAAqE;AACrE,0BAA0B;AAC1B,EAAE;AACF,8DAA8D;AAC9D,2EAA2E;AAC3E,+DAA+D;AAC/D,2EAA2E;AAC3E,wEAAwE;AACxE,0EAA0E;AAC1E,gEAAgE;AAChE,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,2EAA2E;AAC3E,yEAAyE;AAGzE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE;+EAC+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAE/C;2EAC2E;AAC3E,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AAwB9C,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,GAA2B,EAAE,IAA6B;IACzG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,mEAAmE;QACnE,mEAAmE;QACnE,iBAAiB;QACjB,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACpF,CAAC;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACzF,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;QACvC,qEAAqE;QACrE,6DAA6D;QAC7D,OAAO;YACL,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,EAAE,KAAK,EAAE,SAAS,MAAM,sGAAsG,EAAE;YACjL,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,IAAI,EAAE,6BAA6B,EAAE;QAChF,MAAM,EAAE,KAAK;QACb,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,yBAAyB;QACtD,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,OAA2B,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IACD,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QACpH,KAAK,MAAM;YACT,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC7C,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YACpK,CAAC;YACD,iEAAiE;YACjE,iEAAiE;YACjE,gEAAgE;YAChE,6DAA6D;YAC7D,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC1K,KAAK,SAAS;YACZ,OAAO;gBACL,MAAM,EAAE;oBACN,MAAM;oBACN,MAAM,EAAE,aAAa;oBACrB,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,kDAAkD,GAAG,CAAC,MAAM,gCAAgC,CAAC,CAAC,CAAC,wBAAwB,IAAI,CAAC,SAAS,IAAI,yBAAyB,IAAI;oBACpM,UAAU,EAAE,2BAA2B;iBACxC;gBACD,KAAK,EAAE,EAAE;aACV,CAAC;QACJ,KAAK,SAAS;YACZ,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,2BAA2B,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAChI,CAAC;AACH,CAAC;AAED;;2EAE2E;AAC3E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAA6B;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAoB,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,MAAM,KAAK,GAAoB,CAAC,GAAG,UAAU,CAAC,CAAC;IAE/C,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC/D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,qEAAqE;QACrE,mDAAmD;QACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAiB,IAAI,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,4BAA4B,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACjG,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,IAAI,EAAE;QACrD,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,CAAC,GAAG,IAAI,CAAC;YACf,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM;gBAAE,OAAO;YAC7B,IAAI,IAAI,CAAC,CAAC;YACV,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAE3B,uEAAuE;IACvE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC"}
|
|
@@ -28,9 +28,9 @@ export declare class WriteConflictError extends Error {
|
|
|
28
28
|
constructor(currentMtime: string, currentContentHash: string, details: Record<string, unknown>);
|
|
29
29
|
}
|
|
30
30
|
export declare class FileWriteError extends Error {
|
|
31
|
-
readonly code: "stat_failed" | "tmp_write_failed" | "rename_failed" | "audit_write_failed";
|
|
31
|
+
readonly code: "stat_failed" | "tmp_write_failed" | "rename_failed" | "audit_write_failed" | "target_exists";
|
|
32
32
|
readonly details?: Record<string, unknown> | undefined;
|
|
33
|
-
constructor(code: "stat_failed" | "tmp_write_failed" | "rename_failed" | "audit_write_failed", message: string, details?: Record<string, unknown> | undefined);
|
|
33
|
+
constructor(code: "stat_failed" | "tmp_write_failed" | "rename_failed" | "audit_write_failed" | "target_exists", message: string, details?: Record<string, unknown> | undefined);
|
|
34
34
|
}
|
|
35
35
|
export interface FileWriteServiceOpts {
|
|
36
36
|
/** Allowlist roots resolved from env at startup (re-resolved per request is fine; cheap). */
|
|
@@ -53,6 +53,15 @@ export declare class FileWriteService {
|
|
|
53
53
|
* success.
|
|
54
54
|
*/
|
|
55
55
|
writeAtomic(req: FileWriteRequest): FileWriteResult;
|
|
56
|
+
/**
|
|
57
|
+
* Atomically CREATE a new allowlisted file (OPR.0.4.4.20 FR-6 — the frozen
|
|
58
|
+
* review export). Exclusive: throws `target_exists` when the file is
|
|
59
|
+
* already there (frozen exports are point-in-time snapshots, never
|
|
60
|
+
* rewritten; re-invoking the freeze for the same approval is an idempotent
|
|
61
|
+
* no-op at the caller). Same temp-write + fsync + audit-row machinery as
|
|
62
|
+
* writeAtomic — one writer family, no parallel write path.
|
|
63
|
+
*/
|
|
64
|
+
createAtomic(req: Omit<FileWriteRequest, "expectedMtime" | "expectedContentHash">): FileWriteResult;
|
|
56
65
|
private appendAuditRow;
|
|
57
66
|
/** Test/debug helper: returns the configured audit file path. */
|
|
58
67
|
getAuditFilePath(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-write-service.d.ts","sourceRoot":"","sources":["../../../src/domain/files/file-write-service.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,YAAY,EAAE,MAAM;aACpB,kBAAkB,EAAE,MAAM;aAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFhC,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAKnD;AAED,qBAAa,cAAe,SAAQ,KAAK;aAErB,IAAI,EAAE,aAAa,GAAG,kBAAkB,GAAG,eAAe,GAAG,oBAAoB;
|
|
1
|
+
{"version":3,"file":"file-write-service.d.ts","sourceRoot":"","sources":["../../../src/domain/files/file-write-service.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,YAAY,EAAE,MAAM;aACpB,kBAAkB,EAAE,MAAM;aAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFhC,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAKnD;AAED,qBAAa,cAAe,SAAQ,KAAK;aAErB,IAAI,EAAE,aAAa,GAAG,kBAAkB,GAAG,eAAe,GAAG,oBAAoB,GAAG,eAAe;aAEnG,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjC,IAAI,EAAE,aAAa,GAAG,kBAAkB,GAAG,eAAe,GAAG,oBAAoB,GAAG,eAAe,EACnH,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAKpD;AAED,MAAM,WAAW,oBAAoB;IACnC,6FAA6F;IAC7F,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkB;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;gBAErB,IAAI,EAAE,oBAAoB;IAMtC;;;;;;OAMG;IACH,WAAW,CAAC,GAAG,EAAE,gBAAgB,GAAG,eAAe;IAuGnD;;;;;;;OAOG;IACH,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,eAAe,GAAG,qBAAqB,CAAC,GAAG,eAAe;IAqEnG,OAAO,CAAC,cAAc;IAKtB,iEAAiE;IACjE,gBAAgB,IAAI,MAAM;CAG3B;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAE1D"}
|
|
@@ -165,6 +165,83 @@ export class FileWriteService {
|
|
|
165
165
|
byteCountDelta,
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Atomically CREATE a new allowlisted file (OPR.0.4.4.20 FR-6 — the frozen
|
|
170
|
+
* review export). Exclusive: throws `target_exists` when the file is
|
|
171
|
+
* already there (frozen exports are point-in-time snapshots, never
|
|
172
|
+
* rewritten; re-invoking the freeze for the same approval is an idempotent
|
|
173
|
+
* no-op at the caller). Same temp-write + fsync + audit-row machinery as
|
|
174
|
+
* writeAtomic — one writer family, no parallel write path.
|
|
175
|
+
*/
|
|
176
|
+
createAtomic(req) {
|
|
177
|
+
const target = resolveAllowedPath(this.allowlist, req.rootName, req.path);
|
|
178
|
+
if (fs.existsSync(target)) {
|
|
179
|
+
throw new FileWriteError("target_exists", `refusing to overwrite existing file '${target}'`, { target });
|
|
180
|
+
}
|
|
181
|
+
const tmpName = `.openrig-write-${process.pid}-${Math.random().toString(36).slice(2, 10)}-${path.basename(target)}`;
|
|
182
|
+
const tmpPath = path.join(path.dirname(target), tmpName);
|
|
183
|
+
let tmpFd = null;
|
|
184
|
+
try {
|
|
185
|
+
tmpFd = fs.openSync(tmpPath, "w");
|
|
186
|
+
fs.writeFileSync(tmpFd, req.content);
|
|
187
|
+
fs.fsyncSync(tmpFd);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
try {
|
|
191
|
+
if (tmpFd !== null)
|
|
192
|
+
fs.closeSync(tmpFd);
|
|
193
|
+
}
|
|
194
|
+
catch { /* ignore */ }
|
|
195
|
+
try {
|
|
196
|
+
fs.unlinkSync(tmpPath);
|
|
197
|
+
}
|
|
198
|
+
catch { /* ignore */ }
|
|
199
|
+
throw new FileWriteError("tmp_write_failed", `failed to write+fsync temp file at '${tmpPath}': ${err instanceof Error ? err.message : String(err)}`, { target, tmpPath });
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
try {
|
|
203
|
+
if (tmpFd !== null)
|
|
204
|
+
fs.closeSync(tmpFd);
|
|
205
|
+
}
|
|
206
|
+
catch { /* ignore */ }
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
// linkSync fails if the target appeared meanwhile — true exclusive create.
|
|
210
|
+
fs.linkSync(tmpPath, target);
|
|
211
|
+
fs.unlinkSync(tmpPath);
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
try {
|
|
215
|
+
fs.unlinkSync(tmpPath);
|
|
216
|
+
}
|
|
217
|
+
catch { /* ignore */ }
|
|
218
|
+
const code = err.code === "EEXIST" ? "target_exists" : "rename_failed";
|
|
219
|
+
throw new FileWriteError(code, `failed to atomically create '${target}': ${err instanceof Error ? err.message : String(err)}`, { target, tmpPath });
|
|
220
|
+
}
|
|
221
|
+
const newStat = fs.statSync(target);
|
|
222
|
+
const newContent = fs.readFileSync(target);
|
|
223
|
+
const newMtime = newStat.mtime.toISOString();
|
|
224
|
+
const newContentHash = sha256Hex(newContent);
|
|
225
|
+
const auditRow = {
|
|
226
|
+
ts: this.now().toISOString(),
|
|
227
|
+
actor: req.actor,
|
|
228
|
+
root: req.rootName,
|
|
229
|
+
path: req.path,
|
|
230
|
+
absolutePath: target,
|
|
231
|
+
prevMtime: null,
|
|
232
|
+
newMtime,
|
|
233
|
+
prevContentHash: null,
|
|
234
|
+
newContentHash,
|
|
235
|
+
byteCountDelta: newContent.byteLength,
|
|
236
|
+
};
|
|
237
|
+
try {
|
|
238
|
+
this.appendAuditRow(auditRow);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
throw new FileWriteError("audit_write_failed", `create succeeded but audit append failed: ${err instanceof Error ? err.message : String(err)}`, { target, auditFilePath: this.auditFilePath, ...auditRow });
|
|
242
|
+
}
|
|
243
|
+
return { absolutePath: target, newMtime, newContentHash, byteCountDelta: newContent.byteLength };
|
|
244
|
+
}
|
|
168
245
|
appendAuditRow(row) {
|
|
169
246
|
fs.mkdirSync(path.dirname(this.auditFilePath), { recursive: true });
|
|
170
247
|
fs.appendFileSync(this.auditFilePath, `${JSON.stringify(row)}\n`);
|