mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-03-28 03:29:25 +01:00
docs: add shared repo agent contract and handheld development network
This commit is contained in:
parent
159a517744
commit
ef3f63e104
151
AGENTS.md
Normal file
151
AGENTS.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# AGENTS.md
|
||||
|
||||
## Repo Contract
|
||||
|
||||
This repository is a downstream emergency fork of Project N.O.M.A.D.
|
||||
Its product scope is specific:
|
||||
|
||||
- Android-first emergency bootstrap application
|
||||
- hard-offline local read path
|
||||
- local daemon plus loopback API
|
||||
- opportunistic bounded sync
|
||||
- incremental reuse of upstream Project N.O.M.A.D.
|
||||
|
||||
The goal of the agent is not to build generic demos or generic agent platforms.
|
||||
The goal is to build and maintain this emergency runtime in a way that other contributors can understand and extend.
|
||||
|
||||
## Public Agent Tooling
|
||||
|
||||
The agentic files in this repo are versioned on purpose.
|
||||
They are part of the contributor toolkit, not private scratchpad material.
|
||||
|
||||
This includes:
|
||||
|
||||
- `AGENTS.md` at repo root: shared repo-wide contract
|
||||
- `pipeline-handheld/`: project-specific development network for the emergency runtime
|
||||
|
||||
If these files change, keep them coherent with the repo's actual workflow.
|
||||
If the phase model or state schema changes, update the matching docs and state files together.
|
||||
|
||||
## Mission
|
||||
|
||||
- Turn product requests into usable slices of the emergency runtime.
|
||||
- Keep the hard-offline model intact.
|
||||
- Reuse upstream N.O.M.A.D. code where practical through bounded seams.
|
||||
- Leave the repo in a state that another contributor can continue without hidden context.
|
||||
|
||||
## Default Working Style
|
||||
|
||||
- Understand the real repo before changing it.
|
||||
- Prefer execution to discussion once the task is clear.
|
||||
- Make reasonable assumptions when they do not change safety, offline behavior, or upstream compatibility.
|
||||
- Ask for clarification when ambiguity would change:
|
||||
- hard-offline behavior
|
||||
- network policy semantics
|
||||
- upstream seam choices
|
||||
- operator safety or destructive actions
|
||||
- Keep changes scoped to the user request.
|
||||
|
||||
## When To Use `pipeline-handheld/`
|
||||
|
||||
Use `pipeline-handheld/` when the task touches one or more of:
|
||||
|
||||
- offline behavior
|
||||
- `ON` / `OFF` / armed one-shot network policy
|
||||
- local daemon or loopback API
|
||||
- local storage, search, maps, or sync
|
||||
- upstream seam and reuse strategy
|
||||
- multi-file runtime slices that benefit from explicit verify/repair flow
|
||||
|
||||
Do not force `pipeline-handheld/` for:
|
||||
|
||||
- trivial docs
|
||||
- tiny refactors
|
||||
- small one-file fixes
|
||||
- cosmetic edits with no runtime consequence
|
||||
|
||||
For small tasks, direct Builder Mode is preferred.
|
||||
|
||||
## Repo Shape
|
||||
|
||||
Current important areas:
|
||||
|
||||
- `admin/`: upstream N.O.M.A.D. admin/runtime code
|
||||
- `collections/`: upstream content and collection assets
|
||||
- `install/`: upstream installation assets
|
||||
- `docs/emergency/`: emergency runtime docs for this fork
|
||||
- `pipeline-handheld/`: contributor-facing agent network for this runtime
|
||||
|
||||
Prefer additive work in bounded paths instead of broad rewrites of unrelated upstream code.
|
||||
|
||||
## Implementation Priorities
|
||||
|
||||
When building a slice, close the loop as far as the task reasonably allows:
|
||||
|
||||
1. user/operator flow
|
||||
2. contract or seam decision
|
||||
3. backend/runtime behavior
|
||||
4. frontend/UI wiring if relevant
|
||||
5. loading/error/empty states if relevant
|
||||
6. minimal config surface
|
||||
7. local verification
|
||||
|
||||
Do not stop halfway if the slice can be closed end-to-end.
|
||||
|
||||
## Domain Rules
|
||||
|
||||
- Hard-offline read behavior is sacred.
|
||||
- Network policy is security-sensitive. `OFF` and armed one-shot behavior must not be weakened casually.
|
||||
- The network is an ingest path, not the center of the product.
|
||||
- Prefer reuse-first decisions against Project N.O.M.A.D.
|
||||
- Prefer adapters, feature flags, and bounded seams over broad rewrites.
|
||||
- Keep names aligned to the domain, not the tool.
|
||||
|
||||
## Destructive Action Rails
|
||||
|
||||
- Never delete, move, rename, truncate, or regenerate large areas of the repo unless the user explicitly asks.
|
||||
- Never run destructive shell or VCS commands such as `rm`, `git reset --hard`, `git clean`, `git checkout --`, `git restore --source`, or branch deletion unless the user explicitly asks.
|
||||
- Never modify secrets, `.env` files, signing assets, release credentials, or package identifiers unless the user explicitly asks.
|
||||
|
||||
## VCS Write Rails
|
||||
|
||||
- Do not create commits, amend commits, merge, rebase, cherry-pick, tag, push, or open PRs unless the user explicitly asks.
|
||||
- Read-only git inspection is allowed.
|
||||
- Default behavior is to leave changes in the working tree for the user to review and commit.
|
||||
|
||||
## Network Fetch Rails
|
||||
|
||||
- Commands that fetch network context or dependencies in sandbox are known to fail often here.
|
||||
- If a network fetch is required, execute the real fetch path directly with the available permissions flow instead of wasting time on sandbox dry-runs.
|
||||
- Network access is for fetching context or dependencies, not for changing remote state unless the user explicitly asks.
|
||||
|
||||
## Decision Autonomy
|
||||
|
||||
The agent may:
|
||||
|
||||
- create missing files
|
||||
- complete incomplete scaffolds
|
||||
- wire together frontend, backend, daemon, and local API slices
|
||||
- add small development scripts
|
||||
- add mock or fallback behavior when a real integration is not yet configurable
|
||||
- refactor locally when needed to complete the requested slice cleanly
|
||||
|
||||
The agent must not:
|
||||
|
||||
- widen scope arbitrarily
|
||||
- rewrite broad upstream areas without necessity
|
||||
- turn a scoped task into a speculative platform redesign
|
||||
|
||||
## Output Expected
|
||||
|
||||
Each intervention should leave:
|
||||
|
||||
- a usable feature or technical slice
|
||||
- coherent files in the repo
|
||||
- minimal run/test guidance when relevant
|
||||
- a short final explanation focused on what now works, what was verified, and what remains open
|
||||
|
||||
## Summary
|
||||
|
||||
The user owns product direction and commit/publish decisions.
|
||||
The agent builds the terrain: code, docs, seams, wiring, safety rails, and contributor workflow for this emergency bootstrap runtime.
|
||||
46
pipeline-handheld/01-scope-agent/AGENTS.md
Normal file
46
pipeline-handheld/01-scope-agent/AGENTS.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Scope Agent
|
||||
|
||||
## Role
|
||||
Reduce a user request to one concrete development slice for the emergency bootstrap runtime.
|
||||
|
||||
## Input
|
||||
Natural-language task brief.
|
||||
|
||||
## Behavior
|
||||
1. Identify the operator scenario and what part of the emergency runtime is being touched
|
||||
2. Reduce the request to one useful slice
|
||||
3. Ask up to 5 clarification questions only if ambiguity would change offline behavior, network policy, Android/runtime shape, or upstream reuse
|
||||
4. Produce a scoped YAML artifact for downstream seam planning
|
||||
|
||||
## Output Format
|
||||
```yaml
|
||||
project_name: "emergency-bootstrap"
|
||||
request_summary: ""
|
||||
operator_scenario: ""
|
||||
|
||||
slice:
|
||||
name: ""
|
||||
objective: ""
|
||||
touched_surfaces: [] # pwa | daemon | local_api | storage | maps | sync | settings
|
||||
user_flow: []
|
||||
upstream_touchpoints: []
|
||||
non_goals: []
|
||||
|
||||
requirements:
|
||||
hard_offline: []
|
||||
network_policy: []
|
||||
android_device: []
|
||||
loopback_api: []
|
||||
|
||||
acceptance_checks:
|
||||
- ""
|
||||
|
||||
risks:
|
||||
- ""
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Keep it to one slice. Split only if the task is truly too broad.
|
||||
- Hard-offline behavior and network policy are first-class requirements.
|
||||
- If the request implies upstream reuse, say where.
|
||||
- No architecture yet. No code yet.
|
||||
50
pipeline-handheld/02-seam-agent/AGENTS.md
Normal file
50
pipeline-handheld/02-seam-agent/AGENTS.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Seam Agent
|
||||
|
||||
## Role
|
||||
Decide how the requested slice fits this downstream fork: what to reuse from Project N.O.M.A.D., what to adapt, and what to build locally.
|
||||
|
||||
## Input
|
||||
- `state/scope.yaml`
|
||||
- Emergency runtime docs
|
||||
|
||||
## Behavior
|
||||
1. Read the scoped slice and the emergency docs
|
||||
2. Identify upstream touchpoints and the smallest reusable seams
|
||||
3. Choose where to reuse, where to adapt, and where new local code is justified
|
||||
4. Break implementation into at most 3 build slices
|
||||
5. Produce a slice plan that can seed `/state/slices`
|
||||
|
||||
## Output Format
|
||||
```yaml
|
||||
project_name: "emergency-bootstrap"
|
||||
|
||||
slice:
|
||||
name: ""
|
||||
strategy: "" # reuse-first summary
|
||||
|
||||
reuse:
|
||||
upstream_paths: []
|
||||
adapters: []
|
||||
new_local_modules: []
|
||||
|
||||
contracts:
|
||||
local_api_endpoints: []
|
||||
storage_entities: []
|
||||
settings_keys: []
|
||||
|
||||
implementation_slices:
|
||||
- name: ""
|
||||
responsibility: ""
|
||||
touches: []
|
||||
depends_on: []
|
||||
reuse_mode: "" # reuse | adapt | new
|
||||
|
||||
warnings:
|
||||
- ""
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Prefer existing seam docs and additive changes.
|
||||
- Do not invent new subsystems when an adapter will do.
|
||||
- More than 3 implementation slices requires explicit justification.
|
||||
- Be concrete about file paths, local API, storage, and settings when they matter.
|
||||
56
pipeline-handheld/03-runtime-agent/AGENTS.md
Normal file
56
pipeline-handheld/03-runtime-agent/AGENTS.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Runtime Agent
|
||||
|
||||
## Role
|
||||
Produce one end-to-end implementation slice for this repo: Android/PWA, local daemon, loopback API, storage, or sync behavior as needed.
|
||||
|
||||
## Input
|
||||
- One slice manifest from `state/slices`
|
||||
- `state/scope.yaml`
|
||||
- `state/seams.yaml`
|
||||
- Emergency runtime docs
|
||||
|
||||
## Behavior
|
||||
1. Read the slice manifest and honor the chosen reuse mode
|
||||
2. Produce the smallest complete delivery for this slice
|
||||
3. Keep the implementation additive and bounded
|
||||
4. Record touched paths, tests, upstream delta, and any network fetches used
|
||||
5. If blocked, reject instead of widening scope
|
||||
|
||||
## Output Format
|
||||
````md
|
||||
---
|
||||
slice: ""
|
||||
status: "" # built | blocked
|
||||
summary: ""
|
||||
touched_paths: []
|
||||
tests:
|
||||
- path: ""
|
||||
purpose: ""
|
||||
upstream_delta: []
|
||||
destructive_actions_taken: []
|
||||
vcs_actions_taken: []
|
||||
network_fetches: []
|
||||
---
|
||||
# Delivery
|
||||
## Patch Plan
|
||||
- ""
|
||||
|
||||
## Files
|
||||
### <path>
|
||||
```ts
|
||||
// patch-ready code here
|
||||
```
|
||||
|
||||
## Verification Notes
|
||||
- ""
|
||||
|
||||
## Rejection
|
||||
- leave empty if status=built
|
||||
````
|
||||
|
||||
## Rules
|
||||
- No destructive actions.
|
||||
- No VCS write actions.
|
||||
- If a network fetch is required to unblock the slice, execute the real fetch path and record it.
|
||||
- Respect hard-offline behavior and network policy.
|
||||
- Prefer reuse and adaptation over fresh rewrites.
|
||||
54
pipeline-handheld/04-verify-agent/AGENTS.md
Normal file
54
pipeline-handheld/04-verify-agent/AGENTS.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Verify Agent
|
||||
|
||||
## Role
|
||||
Judge whether the built slices are actually correct for this emergency runtime and safe for this repo.
|
||||
|
||||
## Input
|
||||
- `state/scope.yaml`
|
||||
- `state/seams.yaml`
|
||||
- Emergency runtime docs
|
||||
- All slice delivery bundles
|
||||
|
||||
## Behavior
|
||||
1. Check hard-offline fit
|
||||
2. Check network-policy correctness, especially `OFF` and armed one-shot semantics
|
||||
3. Check loopback/API/storage choices against the emergency docs
|
||||
4. Check upstream delta discipline
|
||||
5. Check whether any delivery claims destructive or VCS write actions
|
||||
6. Produce one repo-specific verification report
|
||||
|
||||
## Output Format
|
||||
```yaml
|
||||
status: "" # green | yellow | red
|
||||
summary: ""
|
||||
|
||||
checks:
|
||||
hard_offline: "" # pass | mixed | fail
|
||||
network_policy: "" # pass | mixed | fail
|
||||
android_bootstrap_fit: "" # pass | mixed | fail
|
||||
upstream_delta: "" # pass | mixed | fail
|
||||
repo_safety: "" # pass | mixed | fail
|
||||
|
||||
slice_checks:
|
||||
- name: ""
|
||||
delivery_present: true
|
||||
touched_surfaces_ok: "" # pass | mixed | fail
|
||||
notes: []
|
||||
|
||||
blockers:
|
||||
- ""
|
||||
|
||||
repair_queue:
|
||||
- slice: ""
|
||||
severity: "" # low | medium | high
|
||||
issue: ""
|
||||
suggested_action: ""
|
||||
|
||||
ready_to_apply: true
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Be evidence-based.
|
||||
- A non-empty `destructive_actions_taken` or `vcs_actions_taken` is a repo-safety failure unless the user explicitly asked for it.
|
||||
- A violation of hard-offline semantics or network policy is a functional failure, not a style nit.
|
||||
- Never rewrite code. Only judge and queue repairs.
|
||||
56
pipeline-handheld/05-repair-agent/AGENTS.md
Normal file
56
pipeline-handheld/05-repair-agent/AGENTS.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Repair Agent
|
||||
|
||||
## Role
|
||||
Rewrite one failed slice delivery locally, without widening scope and without taking destructive or VCS write actions.
|
||||
|
||||
## Input
|
||||
- Current `delivery.md` for one slice
|
||||
- `state/scope.yaml`
|
||||
- `state/seams.yaml`
|
||||
- `state/verification.yaml`
|
||||
- Emergency runtime docs
|
||||
|
||||
## Behavior
|
||||
1. Read the verification failure for the target slice
|
||||
2. Patch only the local slice delivery
|
||||
3. Preserve reuse strategy unless verification proves it is wrong
|
||||
4. Keep the fix minimal but complete
|
||||
5. If the real issue is upstream or architectural, block and say so
|
||||
|
||||
## Output Format
|
||||
````md
|
||||
---
|
||||
slice: ""
|
||||
status: "" # patched | blocked
|
||||
repair_summary: ""
|
||||
touched_paths: []
|
||||
tests:
|
||||
- path: ""
|
||||
purpose: ""
|
||||
upstream_delta: []
|
||||
destructive_actions_taken: []
|
||||
vcs_actions_taken: []
|
||||
network_fetches: []
|
||||
---
|
||||
# Patched Delivery
|
||||
## Patch Plan
|
||||
- ""
|
||||
|
||||
## Files
|
||||
### <path>
|
||||
```ts
|
||||
// patched code here
|
||||
```
|
||||
|
||||
## Verification Notes
|
||||
- ""
|
||||
|
||||
## Rejection
|
||||
- leave empty if status=patched
|
||||
````
|
||||
|
||||
## Rules
|
||||
- No destructive actions.
|
||||
- No VCS write actions.
|
||||
- Do not patch sibling slices.
|
||||
- If verification found a design problem rather than a local defect, return `status: blocked`.
|
||||
65
pipeline-handheld/README.md
Normal file
65
pipeline-handheld/README.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Emergency Bootstrap Agent Network
|
||||
|
||||
Personal asset. Not a deliverable. This is a project-specific development network for the Android emergency bootstrap runtime in this fork.
|
||||
|
||||
## Definition
|
||||
Local orchestration pipeline for one domain only:
|
||||
|
||||
- Android-first emergency bootstrap application
|
||||
- hard-offline read path
|
||||
- local daemon + loopback API
|
||||
- bounded sync and network policy
|
||||
- incremental reuse of Project N.O.M.A.D.
|
||||
|
||||
This is not a generic "systems that build systems" pipeline anymore.
|
||||
|
||||
## Flow
|
||||
```
|
||||
Request → [Scope] → [Seam] → [Runtime] → [Verify] → Apply or Ship Review
|
||||
↑ ↑ ↓ ↓
|
||||
└── reject┴──── [Repair] ←──────┘
|
||||
```
|
||||
|
||||
All durable artifacts live in `/state`.
|
||||
|
||||
## Agents
|
||||
|
||||
| # | Agent | Job | Reads from /state | Writes to /state |
|
||||
|---|-------|-----|-------------------|------------------|
|
||||
| 1 | Scope | Turn a request into one emergency-runtime slice | brief | `scope.yaml` |
|
||||
| 2 | Seam | Decide upstream reuse, boundaries, and slice plan | `scope.yaml` + emergency docs | `seams.yaml` |
|
||||
| 3 | Runtime | Build one implementation slice end-to-end | `slices/X.yaml` + project docs | `outputs/X/delivery.md` |
|
||||
| 4 | Verify | Judge offline fit, network-policy correctness, and repo safety | all outputs + project docs | `verification.yaml` |
|
||||
| 5 | Repair | Rewrite one failed slice delivery locally | failed delivery + verification | `outputs/X/delivery.md` |
|
||||
|
||||
## Shared Safety
|
||||
|
||||
Every agent run gets [SAFETY.md](/Users/damzSSD/Projects/emergency-nomad/pipeline-handheld/SAFETY.md) prepended by the router. That file is where destructive-action and VCS-write autonomy are cut down hard.
|
||||
|
||||
## State Layer
|
||||
|
||||
```
|
||||
/state
|
||||
status.json # phase and slice tracking
|
||||
scope.yaml # request reduced to one emergency slice
|
||||
seams.yaml # upstream reuse and slice plan
|
||||
verification.yaml # project-specific verification result
|
||||
slices/ # per-slice manifests seeded from seams.yaml
|
||||
outputs/ # per-slice delivery bundles
|
||||
```
|
||||
|
||||
See `state/STATE.md` for rules.
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/status.py` — inspect live pipeline state
|
||||
- `scripts/router.sh` — runs the agent network with shared safety rails
|
||||
- `scripts/brief.sh` — write a brief and optionally start the pipeline
|
||||
|
||||
## Design Principles
|
||||
|
||||
- Hard-offline is the center of truth.
|
||||
- Upstream reuse is a seam decision, not an afterthought.
|
||||
- Work is organized as repo slices, not abstract services.
|
||||
- Verification is domain-specific: offline behavior, one-shot policy, loopback boundaries, and delta against N.O.M.A.D.
|
||||
- VCS write actions are not autonomous.
|
||||
34
pipeline-handheld/SAFETY.md
Normal file
34
pipeline-handheld/SAFETY.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Emergency Bootstrap Safety Rails
|
||||
|
||||
You are operating inside the downstream emergency fork of Project N.O.M.A.D.
|
||||
This pipeline is for one precise scope: a hard-offline Android bootstrap runtime with a local daemon, loopback API, local corpus, offline maps, and bounded sync.
|
||||
|
||||
## Domain Rails
|
||||
|
||||
- Hard-offline read path is sacred.
|
||||
- Network policy is security-sensitive. `OFF` and armed one-shot behavior must not be weakened casually.
|
||||
- Prefer reuse from upstream Project N.O.M.A.D. through bounded seams, adapters, and additive paths.
|
||||
- Avoid broad rewrites of unrelated upstream areas.
|
||||
|
||||
## Destructive Action Rails
|
||||
|
||||
- Never delete, move, rename, truncate, or regenerate large areas of the repo unless the user explicitly asks.
|
||||
- Never run destructive shell or VCS commands such as `rm`, `git reset --hard`, `git clean`, `git checkout --`, `git restore --source`, or branch deletion unless the user explicitly asks.
|
||||
- Never alter secrets, `.env` files, signing assets, release credentials, or package identifiers unless the user explicitly asks.
|
||||
|
||||
## VCS Write Rails
|
||||
|
||||
- Do not create commits, amend commits, merge, rebase, cherry-pick, tag, push, or open PRs unless the user explicitly asks.
|
||||
- Read-only git inspection is allowed.
|
||||
- Default behavior is to leave changes in the working tree.
|
||||
|
||||
## Network Fetch Rails
|
||||
|
||||
- If a network fetch is required, do not waste time with sandbox dry-runs that are known to fail. Execute the real fetch path directly with the available permissions flow.
|
||||
- Network access is for fetching context or dependencies, never for changing remote state unless the user explicitly asks.
|
||||
|
||||
## Delivery Rails
|
||||
|
||||
- Prefer additive changes in bounded paths.
|
||||
- State clearly when a delivery is a patch-ready bundle versus an already-applied repo mutation.
|
||||
- If the task is ambiguous in a way that would change offline behavior, upstream reuse, or operator safety, reject upstream and ask for clarification.
|
||||
93
pipeline-handheld/USAGE.md
Normal file
93
pipeline-handheld/USAGE.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# USAGE.md — Emergency Bootstrap Network
|
||||
|
||||
## What This Is
|
||||
A stateful agent network tailored to this repo and this runtime profile.
|
||||
|
||||
Use it when the task touches offline behavior, network policy, loopback API, Android/PWA runtime shape, or upstream reuse from Project N.O.M.A.D.
|
||||
|
||||
## Two Modes
|
||||
|
||||
### Mode A — Manual
|
||||
Open a fresh session per phase, load the right `AGENTS.md`, feed the right files from `/state`, save the output back to `/state`.
|
||||
|
||||
### Mode B — CLI via `router.sh` (recommended)
|
||||
The router prepends shared safety rails, manages phase transitions, seeds slice manifests, and persists artifacts.
|
||||
|
||||
```bash
|
||||
# Write your brief
|
||||
echo "Add armed one-shot sync settings to the local daemon and PWA" > brief.txt
|
||||
|
||||
# Run the network
|
||||
./scripts/router.sh scope brief.txt
|
||||
./scripts/router.sh seams
|
||||
./scripts/router.sh build-all
|
||||
./scripts/router.sh verify
|
||||
|
||||
# Or work slice-by-slice
|
||||
./scripts/router.sh build network-policy-ui
|
||||
./scripts/router.sh repair network-policy-ui
|
||||
|
||||
# Status
|
||||
./scripts/router.sh status
|
||||
```
|
||||
|
||||
## Phases
|
||||
|
||||
### 1. Scope
|
||||
- Context: `01-scope-agent/AGENTS.md`
|
||||
- Input: raw task brief
|
||||
- Output: `state/scope.yaml`
|
||||
- Purpose: reduce the request to one emergency-runtime slice with explicit offline and network-policy constraints
|
||||
|
||||
### 2. Seam
|
||||
- Context: `02-seam-agent/AGENTS.md`
|
||||
- Input: `state/scope.yaml` plus the emergency docs
|
||||
- Output: `state/seams.yaml`
|
||||
- Purpose: decide what to reuse from N.O.M.A.D., where to adapt, and which implementation slices exist
|
||||
|
||||
### 3. Runtime
|
||||
- Context: `03-runtime-agent/AGENTS.md`
|
||||
- Input: one slice manifest plus project docs
|
||||
- Output: `state/outputs/<slice>/delivery.md`
|
||||
- Purpose: produce one end-to-end slice delivery bundle for this repo
|
||||
|
||||
### 4. Verify
|
||||
- Context: `04-verify-agent/AGENTS.md`
|
||||
- Input: all slice deliveries plus project docs
|
||||
- Output: `state/verification.yaml`
|
||||
- Purpose: verify hard-offline fit, network policy correctness, upstream delta, and safety compliance
|
||||
|
||||
### 5. Repair
|
||||
- Context: `05-repair-agent/AGENTS.md`
|
||||
- Input: one failed slice delivery plus verification
|
||||
- Output: replacement `state/outputs/<slice>/delivery.md`
|
||||
- Purpose: patch locally without widening scope
|
||||
|
||||
## Safety Rules
|
||||
|
||||
Shared rules live in [SAFETY.md](/Users/damzSSD/Projects/emergency-nomad/pipeline-handheld/SAFETY.md).
|
||||
|
||||
Most important:
|
||||
- no destructive actions without explicit user request
|
||||
- no commits, pushes, rebases, merges, or other VCS writes without explicit user request
|
||||
- network fetches, when needed, should be executed directly rather than sandbox-dry-run first
|
||||
|
||||
## When To Use This
|
||||
|
||||
Use it for:
|
||||
- network policy behavior
|
||||
- daemon/PWA/API slices
|
||||
- upstream seam decisions
|
||||
- offline search/maps/content-sync work
|
||||
|
||||
Do not use it for:
|
||||
- trivial docs
|
||||
- tiny refactors
|
||||
- one-file cosmetic fixes
|
||||
|
||||
## What You Get
|
||||
|
||||
- a scoped request
|
||||
- a seam decision tied to this fork
|
||||
- patch-ready delivery bundles per slice
|
||||
- a verification report that actually cares about the emergency runtime
|
||||
41
pipeline-handheld/scripts/brief.sh
Executable file
41
pipeline-handheld/scripts/brief.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# BRIEF — Interactive brief writer
|
||||
# Writes your idea to brief.txt then optionally kicks off the Scope Agent
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PIPELINE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BRIEF_FILE="$PIPELINE_DIR/brief.txt"
|
||||
|
||||
echo ""
|
||||
echo "=== EMERGENCY BOOTSTRAP BRIEF ==="
|
||||
echo "Describe the emergency-runtime slice you want to build."
|
||||
echo "The Scope Agent will ask clarifying questions only if ambiguity changes offline behavior, network policy, or reuse from N.O.M.A.D."
|
||||
echo ""
|
||||
echo "Type your brief (multi-line). Press CTRL+D when done."
|
||||
echo "---"
|
||||
|
||||
# Read multi-line input
|
||||
BRIEF=""
|
||||
while IFS= read -r line; do
|
||||
BRIEF+="$line"$'\n'
|
||||
done
|
||||
|
||||
if [[ -z "${BRIEF// /}" ]]; then
|
||||
echo "Empty brief. Aborted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$BRIEF" > "$BRIEF_FILE"
|
||||
echo "---"
|
||||
echo "Brief saved to: $BRIEF_FILE"
|
||||
echo ""
|
||||
|
||||
read -p "Run Scope Agent now? [y/N] " -n 1 -r
|
||||
echo ""
|
||||
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
"$PIPELINE_DIR/scripts/router.sh" scope "$BRIEF_FILE"
|
||||
fi
|
||||
602
pipeline-handheld/scripts/router.sh
Executable file
602
pipeline-handheld/scripts/router.sh
Executable file
|
|
@ -0,0 +1,602 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# EMERGENCY BOOTSTRAP ROUTER
|
||||
# Project-specific agent network with shared safety rails.
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PIPELINE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
STATE_DIR="$PIPELINE_DIR/state"
|
||||
STATUS_FILE="$STATE_DIR/status.json"
|
||||
SAFETY_FILE="$PIPELINE_DIR/SAFETY.md"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${CYAN}[router]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
fail() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
||||
|
||||
ensure_state_dirs() {
|
||||
mkdir -p "$STATE_DIR/slices" "$STATE_DIR/outputs"
|
||||
}
|
||||
|
||||
get_phase() {
|
||||
python3 -c "import json; print(json.load(open('$STATUS_FILE'))['phase'])"
|
||||
}
|
||||
|
||||
get_phase_status() {
|
||||
local phase="$1"
|
||||
python3 -c "import json; print(json.load(open('$STATUS_FILE'))['phases'].get('$phase', 'pending'))"
|
||||
}
|
||||
|
||||
update_status() {
|
||||
local phase="$1"
|
||||
local status="$2"
|
||||
python3 -c "
|
||||
import json
|
||||
from datetime import datetime
|
||||
with open('$STATUS_FILE', 'r') as f:
|
||||
state = json.load(f)
|
||||
state['phase'] = '$phase'
|
||||
state['phases']['$phase'] = '$status'
|
||||
state['last_updated'] = datetime.now().isoformat()
|
||||
with open('$STATUS_FILE', 'w') as f:
|
||||
json.dump(state, f, indent=2)
|
||||
"
|
||||
}
|
||||
|
||||
update_slice() {
|
||||
local name="$1"
|
||||
local status="$2"
|
||||
local error="${3:-}"
|
||||
local attempts_delta="${4:-0}"
|
||||
python3 -c "
|
||||
import json
|
||||
from datetime import datetime
|
||||
with open('$STATUS_FILE', 'r') as f:
|
||||
state = json.load(f)
|
||||
slice_info = state.setdefault('slices', {}).setdefault('$name', {
|
||||
'status': 'pending',
|
||||
'attempts': 0,
|
||||
'last_error': ''
|
||||
})
|
||||
slice_info['status'] = '$status'
|
||||
slice_info['attempts'] = slice_info.get('attempts', 0) + int('$attempts_delta')
|
||||
slice_info['last_error'] = '$error'
|
||||
state['last_updated'] = datetime.now().isoformat()
|
||||
with open('$STATUS_FILE', 'w') as f:
|
||||
json.dump(state, f, indent=2)
|
||||
"
|
||||
}
|
||||
|
||||
get_slices() {
|
||||
python3 -c "
|
||||
import json
|
||||
state = json.load(open('$STATUS_FILE'))
|
||||
for name, info in state.get('slices', {}).items():
|
||||
print(f\"{name}:{info.get('status', 'pending')}\")
|
||||
"
|
||||
}
|
||||
|
||||
run_agent() {
|
||||
local agent_name="$1"
|
||||
local agent_md="$2"
|
||||
local input_file="$3"
|
||||
local output_file="$4"
|
||||
local system_prompt
|
||||
|
||||
log "Running $agent_name..."
|
||||
log " System: $agent_md"
|
||||
log " Input: $input_file"
|
||||
log " Output: $output_file"
|
||||
|
||||
[[ -f "$agent_md" ]] || fail "Agent file not found: $agent_md"
|
||||
[[ -f "$input_file" ]] || fail "Input file not found: $input_file"
|
||||
|
||||
if [[ -f "$SAFETY_FILE" ]]; then
|
||||
system_prompt="$(cat "$SAFETY_FILE")"$'\n\n'"$(cat "$agent_md")"
|
||||
else
|
||||
system_prompt="$(cat "$agent_md")"
|
||||
fi
|
||||
|
||||
claude -p --system-prompt "$system_prompt" "$(cat "$input_file")" > "$output_file"
|
||||
|
||||
if [[ -s "$output_file" ]]; then
|
||||
ok "$agent_name completed → $output_file"
|
||||
else
|
||||
fail "$agent_name produced empty output"
|
||||
fi
|
||||
}
|
||||
|
||||
markdown_status() {
|
||||
local delivery_file="$1"
|
||||
python3 -c "
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
|
||||
path = Path('$delivery_file')
|
||||
text = path.read_text(encoding='utf-8') if path.exists() else ''
|
||||
status = 'blocked'
|
||||
if text.startswith('---\\n'):
|
||||
end = text.find('\\n---\\n', 4)
|
||||
if end != -1:
|
||||
meta = yaml.safe_load(text[4:end]) or {}
|
||||
status = meta.get('status', status)
|
||||
print(status)
|
||||
" 2>/dev/null || echo "blocked"
|
||||
}
|
||||
|
||||
yaml_status() {
|
||||
local file="$1"
|
||||
local key="${2:-status}"
|
||||
python3 -c "
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
path = Path('$file')
|
||||
value = 'failed'
|
||||
if path.exists():
|
||||
data = yaml.safe_load(path.read_text(encoding='utf-8')) or {}
|
||||
value = data.get('$key', value)
|
||||
print(value)
|
||||
" 2>/dev/null || echo "failed"
|
||||
}
|
||||
|
||||
sync_project_name_from_yaml() {
|
||||
local file="$1"
|
||||
python3 -c "
|
||||
import json
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
|
||||
status_path = Path('$STATUS_FILE')
|
||||
data_path = Path('$file')
|
||||
|
||||
with open(status_path, 'r') as f:
|
||||
state = json.load(f)
|
||||
|
||||
if data_path.exists():
|
||||
data = yaml.safe_load(data_path.read_text(encoding='utf-8')) or {}
|
||||
project_name = data.get('project_name', '').strip()
|
||||
if project_name:
|
||||
state['project_name'] = project_name
|
||||
|
||||
with open(status_path, 'w') as f:
|
||||
json.dump(state, f, indent=2)
|
||||
"
|
||||
}
|
||||
|
||||
seed_slices_from_seams() {
|
||||
ensure_state_dirs
|
||||
python3 -c "
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
import yaml
|
||||
|
||||
status_path = Path('$STATUS_FILE')
|
||||
seams_path = Path('$STATE_DIR/seams.yaml')
|
||||
slices_dir = Path('$STATE_DIR/slices')
|
||||
|
||||
with open(status_path, 'r') as f:
|
||||
state = json.load(f)
|
||||
|
||||
with open(seams_path, 'r') as f:
|
||||
seams = yaml.safe_load(f) or {}
|
||||
|
||||
slices = seams.get('implementation_slices', [])
|
||||
if not isinstance(slices, list) or not slices:
|
||||
raise SystemExit('seams.yaml does not contain implementation_slices')
|
||||
|
||||
project_name = seams.get('project_name', '').strip()
|
||||
if project_name:
|
||||
state['project_name'] = project_name
|
||||
|
||||
for item in slices:
|
||||
raw_name = str(item.get('name', '')).strip()
|
||||
if not raw_name:
|
||||
continue
|
||||
slice_id = re.sub(r'[^a-zA-Z0-9._-]+', '-', raw_name).strip('-').lower()
|
||||
if not slice_id:
|
||||
continue
|
||||
manifest_path = slices_dir / f'{slice_id}.yaml'
|
||||
payload = dict(item)
|
||||
payload['slice_id'] = slice_id
|
||||
payload['display_name'] = raw_name
|
||||
payload['source_phase'] = 'seams'
|
||||
if not manifest_path.exists():
|
||||
manifest_path.write_text(yaml.safe_dump(payload, sort_keys=False), encoding='utf-8')
|
||||
info = state.setdefault('slices', {}).setdefault(slice_id, {
|
||||
'status': 'pending',
|
||||
'attempts': 0,
|
||||
'last_error': '',
|
||||
'display_name': raw_name,
|
||||
})
|
||||
info.setdefault('display_name', raw_name)
|
||||
|
||||
with open(status_path, 'w') as f:
|
||||
json.dump(state, f, indent=2)
|
||||
"
|
||||
}
|
||||
|
||||
emergency_context_file() {
|
||||
local file="$STATE_DIR/emergency_context.md"
|
||||
{
|
||||
if [[ -f "$PIPELINE_DIR/../docs/emergency/README.md" ]]; then
|
||||
echo "# Emergency Profile"
|
||||
cat "$PIPELINE_DIR/../docs/emergency/README.md"
|
||||
echo
|
||||
fi
|
||||
if [[ -f "$PIPELINE_DIR/../docs/emergency/ARCHITECTURE.md" ]]; then
|
||||
echo "# Emergency Architecture"
|
||||
cat "$PIPELINE_DIR/../docs/emergency/ARCHITECTURE.md"
|
||||
echo
|
||||
fi
|
||||
if [[ -f "$PIPELINE_DIR/../docs/emergency/LOCAL_API.md" ]]; then
|
||||
echo "# Local API"
|
||||
cat "$PIPELINE_DIR/../docs/emergency/LOCAL_API.md"
|
||||
echo
|
||||
fi
|
||||
if [[ -f "$PIPELINE_DIR/../docs/emergency/SEAM_MAP.md" ]]; then
|
||||
echo "# Seam Map"
|
||||
cat "$PIPELINE_DIR/../docs/emergency/SEAM_MAP.md"
|
||||
echo
|
||||
fi
|
||||
if [[ -f "$PIPELINE_DIR/../docs/emergency/COLLECTIONS_SEAM.md" ]]; then
|
||||
echo "# Collections Seam"
|
||||
cat "$PIPELINE_DIR/../docs/emergency/COLLECTIONS_SEAM.md"
|
||||
echo
|
||||
fi
|
||||
} > "$file"
|
||||
printf '%s\n' "$file"
|
||||
}
|
||||
|
||||
seams_input_file() {
|
||||
local file="$STATE_DIR/seams_input.md"
|
||||
local context_file
|
||||
context_file=$(emergency_context_file)
|
||||
{
|
||||
echo "# Scope"
|
||||
cat "$STATE_DIR/scope.yaml"
|
||||
echo
|
||||
echo "# Emergency Docs"
|
||||
cat "$context_file"
|
||||
} > "$file"
|
||||
printf '%s\n' "$file"
|
||||
}
|
||||
|
||||
build_input_for_slice() {
|
||||
local slice="$1"
|
||||
local file="$STATE_DIR/build_input_${slice}.md"
|
||||
local context_file
|
||||
context_file=$(emergency_context_file)
|
||||
{
|
||||
echo "# Scope"
|
||||
cat "$STATE_DIR/scope.yaml"
|
||||
echo
|
||||
echo "# Seams"
|
||||
cat "$STATE_DIR/seams.yaml"
|
||||
echo
|
||||
echo "# Slice Manifest"
|
||||
cat "$STATE_DIR/slices/${slice}.yaml"
|
||||
echo
|
||||
echo "# Emergency Docs"
|
||||
cat "$context_file"
|
||||
} > "$file"
|
||||
printf '%s\n' "$file"
|
||||
}
|
||||
|
||||
verify_input_file() {
|
||||
local file="$STATE_DIR/verify_input.md"
|
||||
local context_file
|
||||
context_file=$(emergency_context_file)
|
||||
{
|
||||
echo "# Scope"
|
||||
cat "$STATE_DIR/scope.yaml"
|
||||
echo
|
||||
echo "# Seams"
|
||||
cat "$STATE_DIR/seams.yaml"
|
||||
echo
|
||||
echo "# Emergency Docs"
|
||||
cat "$context_file"
|
||||
echo
|
||||
echo "# Slice Deliveries"
|
||||
for dir in "$STATE_DIR/outputs"/*; do
|
||||
if [[ -d "$dir" && -f "$dir/delivery.md" ]]; then
|
||||
echo
|
||||
echo "## $(basename "$dir")"
|
||||
cat "$dir/delivery.md"
|
||||
fi
|
||||
done
|
||||
} > "$file"
|
||||
}
|
||||
|
||||
repair_input_for_slice() {
|
||||
local slice="$1"
|
||||
local file="$STATE_DIR/repair_input_${slice}.md"
|
||||
local context_file
|
||||
context_file=$(emergency_context_file)
|
||||
{
|
||||
echo "# Scope"
|
||||
cat "$STATE_DIR/scope.yaml"
|
||||
echo
|
||||
echo "# Seams"
|
||||
cat "$STATE_DIR/seams.yaml"
|
||||
echo
|
||||
echo "# Slice Manifest"
|
||||
cat "$STATE_DIR/slices/${slice}.yaml"
|
||||
echo
|
||||
echo "# Verification"
|
||||
cat "$STATE_DIR/verification.yaml"
|
||||
echo
|
||||
echo "# Current Delivery"
|
||||
cat "$STATE_DIR/outputs/${slice}/delivery.md"
|
||||
echo
|
||||
echo "# Emergency Docs"
|
||||
cat "$context_file"
|
||||
} > "$file"
|
||||
printf '%s\n' "$file"
|
||||
}
|
||||
|
||||
finalize_build_phase() {
|
||||
local result
|
||||
result=$(python3 -c "
|
||||
import json
|
||||
state = json.load(open('$STATUS_FILE'))
|
||||
slices = state.get('slices', {})
|
||||
if not slices:
|
||||
print('failed')
|
||||
else:
|
||||
statuses = {info.get('status', 'pending') for info in slices.values()}
|
||||
if statuses <= {'built', 'patched'}:
|
||||
print('done')
|
||||
elif 'building' in statuses or 'pending' in statuses:
|
||||
print('in_progress')
|
||||
else:
|
||||
print('failed')
|
||||
")
|
||||
|
||||
case "$result" in
|
||||
done)
|
||||
update_status "build" "done"
|
||||
ok "Build phase complete"
|
||||
;;
|
||||
in_progress)
|
||||
update_status "build" "in_progress"
|
||||
warn "Build phase still in progress"
|
||||
;;
|
||||
*)
|
||||
update_status "build" "failed"
|
||||
warn "Build phase has blocked slices"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_scope() {
|
||||
local brief="$1"
|
||||
[[ -f "$brief" ]] || fail "Brief file required. Usage: ./router.sh scope <brief.txt>"
|
||||
update_status "scope" "in_progress"
|
||||
run_agent "Scope Agent" \
|
||||
"$PIPELINE_DIR/01-scope-agent/AGENTS.md" \
|
||||
"$brief" \
|
||||
"$STATE_DIR/scope.yaml"
|
||||
sync_project_name_from_yaml "$STATE_DIR/scope.yaml"
|
||||
update_status "scope" "done"
|
||||
}
|
||||
|
||||
run_seams() {
|
||||
local input_file
|
||||
input_file=$(seams_input_file)
|
||||
update_status "seams" "in_progress"
|
||||
run_agent "Seam Agent" \
|
||||
"$PIPELINE_DIR/02-seam-agent/AGENTS.md" \
|
||||
"$input_file" \
|
||||
"$STATE_DIR/seams.yaml"
|
||||
sync_project_name_from_yaml "$STATE_DIR/seams.yaml"
|
||||
seed_slices_from_seams
|
||||
update_status "seams" "done"
|
||||
rm -f "$input_file"
|
||||
}
|
||||
|
||||
run_build() {
|
||||
local slice="$1"
|
||||
[[ -n "$slice" ]] || fail "Slice name required. Usage: ./router.sh build <slice-name>"
|
||||
|
||||
local manifest="$STATE_DIR/slices/${slice}.yaml"
|
||||
local output_dir="$STATE_DIR/outputs/${slice}"
|
||||
local delivery_file="$output_dir/delivery.md"
|
||||
local input_file
|
||||
|
||||
[[ -f "$manifest" ]] || fail "Slice manifest not found: $manifest"
|
||||
|
||||
ensure_state_dirs
|
||||
mkdir -p "$output_dir"
|
||||
update_status "build" "in_progress"
|
||||
update_slice "$slice" "building" "" "1"
|
||||
|
||||
input_file=$(build_input_for_slice "$slice")
|
||||
run_agent "Runtime Agent" \
|
||||
"$PIPELINE_DIR/03-runtime-agent/AGENTS.md" \
|
||||
"$input_file" \
|
||||
"$delivery_file"
|
||||
|
||||
local delivery_status
|
||||
delivery_status=$(markdown_status "$delivery_file")
|
||||
|
||||
case "$delivery_status" in
|
||||
built)
|
||||
update_slice "$slice" "built"
|
||||
ok "$slice built"
|
||||
;;
|
||||
*)
|
||||
update_slice "$slice" "blocked" "build_blocked"
|
||||
warn "$slice blocked — review delivery bundle"
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$input_file"
|
||||
}
|
||||
|
||||
run_build_all() {
|
||||
update_status "build" "in_progress"
|
||||
seed_slices_from_seams
|
||||
log "Building all pending slices..."
|
||||
while IFS=: read -r name status; do
|
||||
if [[ "$status" == "pending" || "$status" == "failed" || "$status" == "blocked" ]]; then
|
||||
run_build "$name"
|
||||
else
|
||||
log "Skipping $name (status: $status)"
|
||||
fi
|
||||
done <<< "$(get_slices)"
|
||||
finalize_build_phase
|
||||
}
|
||||
|
||||
run_verify() {
|
||||
update_status "verify" "in_progress"
|
||||
verify_input_file
|
||||
run_agent "Verify Agent" \
|
||||
"$PIPELINE_DIR/04-verify-agent/AGENTS.md" \
|
||||
"$STATE_DIR/verify_input.md" \
|
||||
"$STATE_DIR/verification.yaml"
|
||||
|
||||
local verification_status
|
||||
verification_status=$(yaml_status "$STATE_DIR/verification.yaml")
|
||||
case "$verification_status" in
|
||||
green)
|
||||
update_status "verify" "done"
|
||||
ok "Verification green — ready for apply or ship review"
|
||||
;;
|
||||
yellow)
|
||||
update_status "verify" "done"
|
||||
warn "Verification yellow — bounded review remains"
|
||||
;;
|
||||
*)
|
||||
update_status "verify" "failed"
|
||||
warn "Verification red — repair or upstream rethink required"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_repair() {
|
||||
local slice="$1"
|
||||
[[ -n "$slice" ]] || fail "Slice name required. Usage: ./router.sh repair <slice-name>"
|
||||
|
||||
local delivery_file="$STATE_DIR/outputs/${slice}/delivery.md"
|
||||
local input_file
|
||||
|
||||
[[ -f "$delivery_file" ]] || fail "Delivery not found for slice: $slice"
|
||||
|
||||
update_status "repair" "in_progress"
|
||||
update_slice "$slice" "fixing" "" "1"
|
||||
|
||||
input_file=$(repair_input_for_slice "$slice")
|
||||
run_agent "Repair Agent" \
|
||||
"$PIPELINE_DIR/05-repair-agent/AGENTS.md" \
|
||||
"$input_file" \
|
||||
"$delivery_file"
|
||||
|
||||
local delivery_status
|
||||
delivery_status=$(markdown_status "$delivery_file")
|
||||
case "$delivery_status" in
|
||||
patched|built)
|
||||
update_slice "$slice" "patched"
|
||||
update_status "repair" "done"
|
||||
ok "$slice patched — re-run verify"
|
||||
;;
|
||||
*)
|
||||
update_slice "$slice" "blocked" "repair_blocked"
|
||||
update_status "repair" "failed"
|
||||
warn "$slice remains blocked — review delivery bundle"
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$input_file"
|
||||
}
|
||||
|
||||
show_status() {
|
||||
python3 "$PIPELINE_DIR/scripts/status.py"
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: ./router.sh <command> [args]"
|
||||
echo
|
||||
echo "Commands:"
|
||||
echo " scope <brief.txt> Run Scope Agent on a brief"
|
||||
echo " seams Run Seam Agent on scope.yaml + emergency docs"
|
||||
echo " build <slice> Run Runtime Agent on one implementation slice"
|
||||
echo " build-all Run Runtime Agent on all queued slices"
|
||||
echo " verify Run Verify Agent on current deliveries"
|
||||
echo " repair <slice> Run Repair Agent on one blocked slice"
|
||||
echo " status Show current network state"
|
||||
echo " auto Auto-advance to the next meaningful phase"
|
||||
echo
|
||||
}
|
||||
|
||||
auto_advance() {
|
||||
local phase phase_status
|
||||
phase=$(get_phase)
|
||||
phase_status=$(get_phase_status "$phase")
|
||||
|
||||
log "Current phase: $phase ($phase_status)"
|
||||
|
||||
case "$phase" in
|
||||
scope)
|
||||
if [[ "$phase_status" == "done" ]]; then
|
||||
run_seams
|
||||
else
|
||||
fail "Scope not done yet. Run: ./router.sh scope <brief.txt>"
|
||||
fi
|
||||
;;
|
||||
seams)
|
||||
if [[ "$phase_status" == "done" ]]; then
|
||||
run_build_all
|
||||
else
|
||||
fail "Seams not done yet. Run: ./router.sh seams"
|
||||
fi
|
||||
;;
|
||||
build)
|
||||
if [[ "$phase_status" == "done" ]]; then
|
||||
run_verify
|
||||
else
|
||||
warn "Build is not done yet. Resolve blocked slices or re-run build-all."
|
||||
fi
|
||||
;;
|
||||
verify)
|
||||
if [[ "$phase_status" == "done" ]]; then
|
||||
ok "Verification complete — ready for apply or ship review"
|
||||
else
|
||||
warn "Verification failed — use repair or rethink seams"
|
||||
fi
|
||||
;;
|
||||
repair)
|
||||
if [[ "$phase_status" == "done" ]]; then
|
||||
run_verify
|
||||
else
|
||||
warn "Repair phase failed — review the blocked slice"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "Unknown phase: $phase"
|
||||
show_status
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "${1:-status}" in
|
||||
scope) run_scope "${2:-}" ;;
|
||||
seams) run_seams ;;
|
||||
build) run_build "${2:-}" ;;
|
||||
build-all) run_build_all ;;
|
||||
verify) run_verify ;;
|
||||
repair) run_repair "${2:-}" ;;
|
||||
status) show_status ;;
|
||||
auto) auto_advance ;;
|
||||
help|-h) usage ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
66
pipeline-handheld/scripts/status.py
Normal file
66
pipeline-handheld/scripts/status.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Status reader for the emergency bootstrap network."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
STATE_DIR = os.path.join(os.path.dirname(__file__), '..', 'state')
|
||||
STATUS_FILE = os.path.join(STATE_DIR, 'status.json')
|
||||
|
||||
def read_status():
|
||||
if not os.path.exists(STATUS_FILE):
|
||||
print("No status.json found. Pipeline not started.")
|
||||
return
|
||||
|
||||
with open(STATUS_FILE, 'r') as f:
|
||||
status = json.load(f)
|
||||
|
||||
print(f"=== {status.get('project_name', 'Unnamed Project')} ===")
|
||||
print(f"Schema: v{status.get('schema_version', '?')}")
|
||||
print(f"Current phase: {status.get('phase', '?')}")
|
||||
print(f"Last updated: {status.get('last_updated', 'never')}")
|
||||
print()
|
||||
|
||||
# Phase overview
|
||||
print("PHASES:")
|
||||
phases = status.get('phases', {})
|
||||
phase_icons = {
|
||||
"done": "✓",
|
||||
"in_progress": "→",
|
||||
"failed": "✗",
|
||||
"pending": "·",
|
||||
}
|
||||
for phase, state in phases.items():
|
||||
icon = phase_icons.get(state, "?")
|
||||
print(f" {icon} {phase}: {state}")
|
||||
print()
|
||||
|
||||
# Slices
|
||||
slices = status.get('slices', {})
|
||||
if slices:
|
||||
print("SLICES:")
|
||||
for name, info in slices.items():
|
||||
s = info.get('status', '?')
|
||||
attempts = info.get('attempts', 0)
|
||||
icon = {
|
||||
"built": "✓",
|
||||
"patched": "✓",
|
||||
"building": "→",
|
||||
"fixing": "⚡",
|
||||
"blocked": "✗",
|
||||
"failed": "✗",
|
||||
"pending": "·",
|
||||
}.get(s, "?")
|
||||
line = f" {icon} {name}: {s} (attempts: {attempts})"
|
||||
if info.get('last_error'):
|
||||
line += f" — {info['last_error']}"
|
||||
if info.get('display_name') and info['display_name'] != name:
|
||||
line += f" [{info['display_name']}]"
|
||||
print(line)
|
||||
print()
|
||||
|
||||
if status.get('notes'):
|
||||
print(f"Notes: {status['notes']}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
read_status()
|
||||
55
pipeline-handheld/state/STATE.md
Normal file
55
pipeline-handheld/state/STATE.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# /state — Emergency Bootstrap State Layer
|
||||
|
||||
This folder is the spine of the project-specific network. Every phase reads from here and writes to here.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
/state
|
||||
status.json # where you are, what slices are blocked
|
||||
scope.yaml # reduced task scope for this runtime
|
||||
seams.yaml # upstream reuse and slice plan
|
||||
verification.yaml # repo-specific verification result
|
||||
slices/
|
||||
slice-a.yaml # seeded manifest per implementation slice
|
||||
slice-b.yaml
|
||||
outputs/
|
||||
slice-a/
|
||||
delivery.md # current delivery bundle for the slice
|
||||
slice-b/
|
||||
delivery.md
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
1. Every phase reads input from `/state`
|
||||
2. Every phase writes durable output to `/state`
|
||||
3. `status.json` is updated after every phase transition
|
||||
4. If it is not in `/state`, downstream cannot rely on it
|
||||
5. Slice manifests are seeded from `seams.yaml`
|
||||
6. Safety rails still apply even when a delivery is blocked
|
||||
|
||||
## Status Values
|
||||
|
||||
### Phase status
|
||||
- `pending`
|
||||
- `in_progress`
|
||||
- `done`
|
||||
- `failed`
|
||||
|
||||
### Slice status
|
||||
- `pending`
|
||||
- `building`
|
||||
- `built`
|
||||
- `blocked`
|
||||
- `fixing`
|
||||
- `patched`
|
||||
- `failed`
|
||||
|
||||
## Resume Rule
|
||||
|
||||
1. Open `status.json`
|
||||
2. Find current phase and any blocked slices
|
||||
3. Load the right `AGENTS.md`
|
||||
4. Feed it the relevant artifacts from `/state`
|
||||
5. Continue from the last durable artifact, not from memory
|
||||
15
pipeline-handheld/state/status.json
Normal file
15
pipeline-handheld/state/status.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"schema_version": "3.1",
|
||||
"project_name": "emergency-bootstrap",
|
||||
"phase": "scope",
|
||||
"phases": {
|
||||
"scope": "pending",
|
||||
"seams": "pending",
|
||||
"build": "pending",
|
||||
"verify": "pending",
|
||||
"repair": "pending"
|
||||
},
|
||||
"slices": {},
|
||||
"last_updated": "",
|
||||
"notes": ""
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user