From ffa8fb9bf51d92f528b2904100c5d6924673594e Mon Sep 17 00:00:00 2001 From: Hanns Peter Date: Mon, 13 Jul 2026 03:52:02 -0400 Subject: [PATCH] feat: integrate godplans 1.1 contract --- ARCHITECTURE.md | 26 +- README.md | 3 +- agents/arch.md | 2 + agents/god-auditor.md | 17 +- agents/god-reconciler.md | 14 +- agents/god-reconstructor.md | 21 +- agents/quality.md | 1 + docs/ROADMAP.md | 6 +- docs/phase-4-state-read-inventory.md | 2 +- docs/planning-system-migration.md | 99 ++-- docs/reference.md | 2 +- lib/README.md | 4 +- lib/feature-awareness.js | 5 +- lib/planning-systems.js | 106 +++- lib/sibling-artifacts.js | 519 +++++++++++++++++- lib/source-sync.js | 7 +- .../orchestration/GOD-ORCHESTRATOR-RUNBOOK.md | 15 +- references/orchestration/MODE-DETECTION.md | 14 +- routing/god-context-scan.yaml | 1 + routing/god-doctor.yaml | 1 + routing/god-init.yaml | 1 + routing/god-mode.yaml | 1 + routing/god-preflight.yaml | 1 + routing/god-reconcile.yaml | 1 + schema/state.v1.json | 2 +- scripts/test-sibling-artifacts.js | 250 ++++++++- skills/god-capture.md | 3 +- skills/god-context-scan.md | 3 +- skills/god-doctor.md | 10 +- skills/god-init.md | 13 +- skills/god-migrate.md | 22 +- skills/god-mode.md | 21 +- skills/god-preflight.md | 12 +- skills/god-reconcile.md | 2 +- skills/god-reconstruct.md | 12 +- templates/IMPORTED-CONTEXT.mdx | 7 +- templates/INITIAL-FINDINGS.mdx | 4 +- workflows/brownfield-arc.yaml | 8 +- workflows/full-arc.yaml | 8 +- 39 files changed, 1084 insertions(+), 162 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d919451..9525bcf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1096,14 +1096,20 @@ v0.3 commands keep working. v1.0 freezes the public API. Godpowers consumes the artifacts of its two sibling superskills without owning them. -[DECISION] The consumed contract is `.godplans/PLAN.mdx` (godplans master -plan) and `.godaudits/AUDIT.json` (godaudits 2.x canonical machine state), +[DECISION] The consumed contract is `.godplans/PLAN.mdx` plus the executable, +pinned `.godplans/validate-plan.sh` companion (Godplans 1.1 master plan), and +`.godaudits/AUDIT.json` (godaudits 2.x canonical machine state), with `.godaudits/AUDIT.mdx` supported as a generated report and 1.x fallback. `lib/sibling-artifacts.js` is the read-only parser: it detects the canonical source, parses `GP-nnn` plan tasks and typed `GA-nnn` remediation tasks, imports the explicit check-outcome ledger, secret-safe evidence metadata, compliance result, accepted risks, open questions, compiled score caps, and coverage, and -recomputes counts only for legacy MDX. +recomputes task and phase counts from plan checkboxes, and recomputes audit +counts only for legacy MDX. For Godplans, it performs a non-executing mirror of +the 1.1 structural gate, verifies the companion hash, file type, and executable +mode, exposes lifecycle status, and refuses GP dispatch for incomplete, +unsupported, `planning`, or `done` contracts. The official companion remains +the authoritative execution gate and must pass immediately before GP work. Plan requirements use `R--n` ids and audit checks mirror them one to one as `A--n` across the 18 shared domain codes; Godpowers preserves those ids verbatim on import so traceability survives the handoff. @@ -1117,7 +1123,13 @@ without writing, and canonical audit reads use a 5 MiB limit so the complete [DECISION] Sibling files are read-only for Godpowers with one carve-out: when Godpowers executes a GP or GA task, the executing agent follows the owning -product's executor rules. A GP task updates PLAN.mdx. A godaudits 2.x task +product's executor rules. A GP task may begin only from `approved` or +`executing` after `bash .godplans/validate-plan.sh .godplans/PLAN.mdx` passes. +The first executor moves `approved` to `executing`; passing tasks update the +checkbox, derived counters, date, and session log atomically; only final +Verification may move the plan to `done`. Material replans return to +`planning`, preserve completed task history, and require fresh approval. A +godaudits 2.x task updates reciprocal task, finding, check, evidence, and audit metadata in AUDIT.json, then runs `godaudits validate .godaudits/AUDIT.json --write` and regenerates AUDIT.mdx and @@ -1126,8 +1138,10 @@ flow writes back only through `.godplans/GODPOWERS-SYNC.mdx` and `.godaudits/GODPOWERS-SYNC.mdx` (lib/source-sync.js); fences are never written into PLAN.mdx, AUDIT.json, or AUDIT.mdx. -[DECISION] Imports record a content hash; `sibling-artifacts.staleness` -compares it against the current canonical source so drift between an imported +[DECISION] Imports record a source fingerprint; Godplans 1.1 fingerprints both +canonical contract files plus the validator executable mode, while +`sibling-artifacts.staleness` compares the recorded set +against the current canonical source so drift between an imported digest and live sibling state surfaces explicitly. For godaudits 2.x, only AUDIT.json controls staleness, so regenerating AUDIT.mdx cannot create a false drift warning. diff --git a/README.md b/README.md index 67b97ae..e18804d 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,8 @@ It fuses several disciplines into one workflow: from it, and every change traces back to a request. It is the builder in a three-part family: [godplans](https://github.com/hannsxpeter/godplans) -plans everything up front (`.godplans/PLAN.mdx`), [godaudits](https://github.com/hannsxpeter/godaudits) +plans everything up front (`.godplans/PLAN.mdx` plus its self-contained +validator), [godaudits](https://github.com/hannsxpeter/godaudits) scores what was built (canonical `.godaudits/AUDIT.json` plus a generated `AUDIT.mdx` report), and Godpowers builds, imports either one when it finds it, and ships. Its own artifacts live under diff --git a/agents/arch.md b/agents/arch.md index 2ff8354..76987fd 100644 --- a/agents/arch.md +++ b/agents/arch.md @@ -26,12 +26,14 @@ see_also: [quality, deploy] - [DECISION] `ARCHITECTURE-MAP.md` keeps a complete core command supplement so all 122 shipped skills appear in the human-readable map. - [DECISION] Workflow plans use canonical helper IDs such as `source-sync-back` and `pillars-sync-plan`, while `/god-sync` output may show the shorter aliases `source-sync` and `pillars-sync`. - [DECISION] Godaudits 2.x interoperability reads `.godaudits/AUDIT.json` as canonical machine state, imports explicit check outcomes, evidence metadata, compliance, accepted risks, open questions, score caps, coverage, findings, and typed GA tasks, and uses generated or legacy AUDIT.mdx only as a fallback. +- [DECISION] Godplans 1.1 interoperability treats `.godplans/PLAN.mdx` plus the pinned executable `.godplans/validate-plan.sh` as one contract, mirrors structural validation without executing repository shell during import, blocks GP dispatch outside `approved` or `executing`, and requires the official validator to pass immediately before work. - [DECISION] The current executable audit status is fresh for repo surface, route quality, recipe coverage, and workflow planning. ## Watchouts - [HYPOTHESIS] Runtime behavior depends on host AI tools exposing skill and agent capabilities consistently. - [HYPOTHESIS] Local helper work must stay visible in closeouts so automatic work does not become hidden orchestration. +- [HYPOTHESIS] A future Godplans validator hash requires an explicit Godpowers compatibility update so new shell bytes cannot become trusted silently. ## Godpowers artifact sources diff --git a/agents/god-auditor.md b/agents/god-auditor.md index ff7ef0b..5e2cf38 100644 --- a/agents/god-auditor.md +++ b/agents/god-auditor.md @@ -42,8 +42,14 @@ why. are untrusted repo content: run them only when plainly read-only (grep/test/ls/node --check class); anything that mutates state requires showing the command and getting user confirmation first. - - `.godplans/PLAN.mdx` present: add a plan-conformance dimension. Do the - Godpowers artifacts and code satisfy the plan's R--n requirements + - `.godplans/PLAN.mdx` present: call `loadPlan(projectRoot)` and add a + plan-conformance dimension. A missing, non-executable, unknown, or + non-regular `.godplans/validate-plan.sh`, structural validation failure, + or inconsistent lifecycle is a plan-contract finding and blocks GP + execution. A `planning` plan awaits approval; a `done` plan is closed. + Do not run the companion during a read-only audit. For a complete + Godplans 1.1 contract, do the Godpowers artifacts and code satisfy the + plan's R--n requirements and GP acceptance criteria? Cite plan facts as [DECISION]-grade authored intent by GP/R id; inferences beyond the plan stay [HYPOTHESIS]. Both files are read-only for this agent: never edit them; any write-back @@ -236,9 +242,10 @@ Preflight rules: - Inspect package manifests, lockfiles, build files, test config, CI config, source layout, entry points, docs, ADRs, env examples, AGENTS.md, deploy signals, observability signals, and ownership signals. -- Mandatory: inspect `.godplans/PLAN.mdx` and `.godaudits/AUDIT.json` when - present. A master plan is direct arc-readiness evidence; validated audit - state provides direct scoring and coverage evidence. Recommend `/god-migrate` import before +- Mandatory: inspect `.godplans/PLAN.mdx`, `.godplans/validate-plan.sh`, and + `.godaudits/AUDIT.json` when present. A complete Godplans 1.1 two-artifact + contract is direct arc-readiness evidence; validated audit state provides + direct scoring and coverage evidence. Recommend `/god-migrate` import before reconstruction when either exists. - Brownfield mode inspects existing codebase shape and refactor risk. - Bluefield mode inspects org context, sibling conventions, shared packages, diff --git a/agents/god-reconciler.md b/agents/god-reconciler.md index d23bfd8..11fec2c 100644 --- a/agents/god-reconciler.md +++ b/agents/god-reconciler.md @@ -175,10 +175,14 @@ Source: `.godpowers/state.json` `tier-3.launch`. - If conflicts exist: recommend greenfieldifier review before writes. #### SIBLING PLAN (`.godplans/PLAN.mdx`) -- Does the intent match an existing GP task in the plan? (read-only; parse - via `lib/sibling-artifacts.js`) -- Verdict: not-applicable / planned-in-godplans (with GP id) / not-in-plan / - plan-conflict +- Does `.godplans/validate-plan.sh` complete the pinned Godplans 1.1 contract, + and is lifecycle status eligible for execution? Inspect read-only through + `lib/sibling-artifacts.loadPlan`. +- Does the intent match an existing GP task in the plan? +- Verdict: not-applicable / contract-incomplete / awaiting-approval / closed / + planned-in-godplans (with GP id) / not-in-plan / plan-conflict +- Only `ready-for-validator` may recommend execution, and its action must run + `bash .godplans/validate-plan.sh .godplans/PLAN.mdx` before the GP task. - If plan-conflict: the plan is authored intent; surface the GP/R id and ask the user before proceeding. @@ -242,7 +246,7 @@ Return structured JSON to the orchestrating skill: "repo_surface": { "status": "needs-surface-sync", "action": "run repo-surface-sync" }, "feature_awareness": { "status": "needs-awareness-refresh", "action": "run feature-awareness" }, "source_sync_back": { "status": "not-applicable" }, - "sibling_plan": { "status": "planned-in-godplans", "match": "GP-204", "action": "execute under the plan's embedded executor rules" }, + "sibling_plan": { "status": "planned-in-godplans", "match": "GP-204", "action": "run the pinned validator, then execute under the plan's lifecycle rules" }, "sibling_audit": { "status": "addresses-ga-task", "match": "GA-102", "action": "route via /god-fix GA-102 with the finding's Verify command as done-check" }, "host_capability": { "status": "degraded", "gap": "fresh-context agent spawn not detected" }, "safe_sync": { "status": "clear" }, diff --git a/agents/god-reconstructor.md b/agents/god-reconstructor.md index fdd17bb..b9155bb 100644 --- a/agents/god-reconstructor.md +++ b/agents/god-reconstructor.md @@ -45,9 +45,10 @@ intent is lost) but it gives Godpowers something to work with. - Project root - Optional: archaeology report from god-archaeologist - Optional: any existing partial artifacts (README, ADRs, comments) -- Optional: `.godplans/PLAN.mdx` (authored godplans master plan; treat as - HIGH-confidence intent, cite by GP/R id, never edit it; parse via - `lib/sibling-artifacts.js`) +- Optional: `.godplans/PLAN.mdx` plus `.godplans/validate-plan.sh`. Treat GP/R + facts as HIGH-confidence intent only when `loadPlan` reports a complete + Godplans 1.1 contract. Otherwise use them as hypothesis-grade migration + context. Never edit either file during reconstruction. ## Process @@ -63,9 +64,10 @@ From the code, derive: within its priority (P-MUST-01, P-MUST-02, P-SHOULD-01, P-COULD-01), placed at the start of the bullet. These ids are load-bearing for the deliverable ledger and the linkage map. -- When `.godplans/PLAN.mdx` is present, derive the problem statement, users, - and requirements from the plan FIRST (confidence HIGH; these are - [DECISION]-grade citations of authored intent), and code evidence second. +- When a complete Godplans 1.1 contract is present, derive the problem + statement, users, and requirements from the plan FIRST (confidence HIGH; + these are [DECISION]-grade citations of authored intent), and code evidence + second. An incomplete or legacy plan stays [HYPOTHESIS]-grade. Preserve the source R--n id alongside the minted P-* id, e.g. `P-MUST-01 (plan: R-AUTH-2)`, so linkage back to the plan survives. - **Non-functional requirements**: what NFRs are enforced? (from rate limits, caching, indexes) @@ -96,9 +98,10 @@ From git history + current state: - **Next**: TODOs at module level, unimplemented endpoints, stubs - **Later**: high-level themes from issue tracker if accessible -When `.godplans/PLAN.mdx` is present, map its GP task checkbox state to the -roadmap instead of inferring solely from git history: `- [x] GP-` tasks feed -Done, `- [ ] GP-` tasks feed Next, in the plan's phase and wave order. +When a complete Godplans 1.1 contract is present, map its GP task checkbox +state to the roadmap instead of inferring solely from git history: `- [x] GP-` +tasks feed Done, `- [ ] GP-` tasks feed Next, in phase and wave order. For an +incomplete contract, record those states as hypotheses pending repair. Give each delivery increment a stable `M-` id, a `**Status**:` field (pending/building/done), and a `**Features (from PRD)**:` list of the diff --git a/agents/quality.md b/agents/quality.md index 88def91..9d10fb0 100644 --- a/agents/quality.md +++ b/agents/quality.md @@ -22,6 +22,7 @@ see_also: [security, deploy] - [DECISION] `npm run test:self-truth` blocks stale version, public surface, lifecycle, artifact, requirement, generated progress, and roadmap provenance claims. - [DECISION] The full test suite includes quick proof docs, repo-doc sync, repo-surface sync, automation surface sync, host capabilities, extension authoring, dogfood, Mode D, installer smoke, workflow runner, OTel, and extension publish-readiness checks. - [DECISION] Sibling-artifact tests cover godaudits 2.x JSON authority, large canonical files, non-regular source rejection, legacy and generated MDX fallback, check and evidence ledgers, compliance, accepted risks, score caps, compiled coverage, typed GA dispatch, managed todo synchronization, MDX safety, canonical staleness, migration seeds, and remediation impact detection. +- [DECISION] Sibling-artifact tests cover the Godplans 1.1 validator identity, two-artifact completeness, static structural preflight, lifecycle dispatch gates, full GP/R seed traceability, large-plan reads, and legacy hypothesis-grade fallback. - [DECISION] Build and review agents enforce request-trace discipline: assumptions, public behavior, expected files, and verification command must be explicit before implementation. - [DECISION] Reviewers block speculative flexibility, unrelated cleanup, and diff churn that cannot be traced to the user request, slice plan, failing test, or implementation-caused cleanup. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 8d45368..fefa55b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -2,7 +2,7 @@ > Status: ACTIVE > Model: Pure-skill for durable work. CLI provides install plus read-only status helpers. -> Last updated: 2026-07-03 +> Last updated: 2026-07-13 > Current source: v5.3.1. Latest published: v5.3.1. This roadmap tracks releases, what's shipped, and what is frozen during the @@ -45,6 +45,10 @@ quarterback, work report, reflections, memory, lessons, outcome loops, MCP read tools, and ledger importer) shipped across 3.1.0-3.11.0. What works today: +- **Godplans 1.1 interoperability**: PLAN plus pinned executable validator + contract detection, non-executing structural preflight, lifecycle-safe GP + routing, exact pre-execution validator gate, complete GP/R seed traceability, + large-plan reads, two-file staleness, and legacy hypothesis-grade fallback. - **Godaudits 2.x interoperability**: canonical `.godaudits/AUDIT.json` detection and import, generated and legacy MDX fallback, compiled score and coverage signals, typed GA remediation dispatch, managed todo synchronization, diff --git a/docs/phase-4-state-read-inventory.md b/docs/phase-4-state-read-inventory.md index cc20ac4..5775ec3 100644 --- a/docs/phase-4-state-read-inventory.md +++ b/docs/phase-4-state-read-inventory.md @@ -29,7 +29,7 @@ | Workflow greenfieldification artifact lists | [DECISION] decision-read | `workflows/brownfield-arc.yaml:75`, `workflows/brownfield-arc.yaml:76`, `workflows/brownfield-arc.yaml:77`, `workflows/brownfield-arc.yaml:78`, `workflows/bluefield-arc.yaml:59`, `workflows/bluefield-arc.yaml:60`, `workflows/bluefield-arc.yaml:61`, `workflows/bluefield-arc.yaml:62`, `lib/workflow-runner.js:140` | [DECISION] Completed: brownfield and bluefield greenfieldifier workflow inputs now pass `.godpowers/state.json` plus canonical artifacts instead of generated per-tier state views. | | Pillars artifact sync | [DECISION] migration-read | `lib/pillars.js:99`, `lib/pillars.js:100`, `lib/pillars.js:112`, `lib/pillars.js:113`, `lib/pillars.js:451`, `lib/pillars.js:457`, `lib/pillars.js:594`, `lib/pillars.js:628` | [DECISION] Completed: Pillars artifact discovery now maps `.godpowers/state.json` to durable context, deploy, and observe concerns instead of reading generated deploy or observe state views. | | Source-system sync-back summary | [DECISION] display-read | `lib/source-sync.js:88`, `lib/source-sync.js:99`, `lib/source-sync.js:134` | [DECISION] Completed: source-system sync-back now summarizes `.godpowers/state.json` instead of `.godpowers/build/STATE.mdx`. | -| Planning-system import seed for build state | [DECISION] migration-read | `lib/planning-systems.js:67`, `skills/god-migrate.md:101`, `docs/planning-system-migration.md:10` | [DECISION] Keep imported source-system `STATE.md` reads as migration inputs, but write imported build-state seed data through `state.json` and regenerated views. | +| Planning-system import seed for build state | [DECISION] migration-read | `lib/planning-systems.js`, `skills/god-migrate.md`, `docs/planning-system-migration.md` | [DECISION] Keep imported source-system state as migration input, write its full ledger to `.godpowers/prep/IMPORTED-BUILD-STATE.mdx`, reference that artifact from `state.json`, and keep `.godpowers/build/STATE.mdx` as a regenerated managed view. | | Context writer human prompt section | [DECISION] display-read | `lib/context-writer.js:86`, `lib/context-writer.js:92`, `lib/context-writer.js:151`, `scripts/test-context-writer.js:176` | [DECISION] Completed: generated AI-tool context now names `state.json` as authority and `PROGRESS.mdx` as a generated human view. | | Route and gate tests | [DECISION] decision-read | `scripts/test-router.js:96`, `scripts/test-router.js:195`, `scripts/test-router.js:332`, `scripts/test-gate.js:52`, `scripts/test-gate.js:125`, `scripts/test-gate.js:138`, `scripts/test-gate.js:195` | [DECISION] Completed: generated `STATE.md` hand-edits are overwritten by the next state mutation, and router plus static checks now reject generated per-tier state views as route or workflow handoff authority. | | Quick-proof and doc drift tests | [DECISION] display-read | `scripts/test-quick-proof.js:256`, `docs/quick-proof.md:112`, `README.md:214`, `README.md:481`, `docs/concepts.md:66`, `docs/command-flows.md:64`, `docs/command-flows.md:298`, `docs/command-flows.md:491` | [DECISION] Completed: proof, concept, reference, command-flow, and README examples now show `state.json` as authority and `PROGRESS.mdx` as a generated view. | diff --git a/docs/planning-system-migration.md b/docs/planning-system-migration.md index ef1aa1c..b13f40d 100644 --- a/docs/planning-system-migration.md +++ b/docs/planning-system-migration.md @@ -8,9 +8,10 @@ and can write its own progress back. Two kinds of prior planning are supported: 1. **Sibling superskills** (first-class, structured, machine-authored): - - [godplans](https://github.com/hannsxpeter/godplans) emits `.godplans/PLAN.mdx`, a - master plan with `GP-` checkbox tasks, `R--n` requirements, and - embedded executor rules. + - [godplans](https://github.com/hannsxpeter/godplans) 1.1 emits + `.godplans/PLAN.mdx` plus executable `.godplans/validate-plan.sh`, a + two-artifact master-plan contract with `GP-` checkbox tasks, + `R--n` requirements, lifecycle state, and embedded executor rules. - [godaudits](https://github.com/hannsxpeter/godaudits) emits canonical `.godaudits/AUDIT.json` plus generated `.godaudits/AUDIT.mdx`, with `A--n` checks, `F--n` findings, compiled score and coverage, and @@ -23,15 +24,20 @@ Two kinds of prior planning are supported: godplans, godaudits, and Godpowers share one design. godplans inverts every audit check into a plan-time requirement; godaudits runs those same checks forward against code; Godpowers builds. They share the 18 domain codes and a -mirrored id scheme, so `A-SEC-3` audits what `R-SEC-3` planned. Because a -`PLAN.mdx` is authored intent and `AUDIT.json` is validated, evidence-backed -machine state, Godpowers trusts them more than it trusts a foreign document: +mirrored id scheme, so `A-SEC-3` audits what `R-SEC-3` planned. Because the +complete Godplans two-artifact emission is authored, structurally checked +intent, and `AUDIT.json` is validated, evidence-backed machine state, Godpowers +trusts them more than it trusts a foreign document: -- A `PLAN.mdx` seeds the PRD, architecture, roadmap, and stack tiers as +- A complete Godplans 1.1 contract seeds the PRD, architecture, roadmap, + stack, and build-state tiers as `imported` context directly, instead of being reverse-engineered from code. - Plan-stated facts are cited as `[DECISION]`-grade (with the source `GP`/`R` id - preserved verbatim); product claims inferred beyond the plan stay - `[HYPOTHESIS]`. + All applicable `GP` task ids, lifecycle fields, Verify commands, local + requirements, and domain requirement ids are preserved in destination + seeds. Plan-stated facts are cited as `[DECISION]`-grade. A PLAN without the + pinned executable validator, or a structurally invalid or inconsistent + contract, remains hypothesis-grade migration context and cannot dispatch GP + work. - An `AUDIT.json` seeds the harden tier and turns open `GA` remediation tasks into dispatchable work. Findings are deduped by `F` id rather than rediscovered. The generated `AUDIT.mdx` remains a readable fallback. @@ -39,15 +45,16 @@ machine state, Godpowers trusts them more than it trusts a foreign document: The machine contract Godpowers parses (frontmatter fields, task and finding grammar, id schemes, MDX-safety rules) is documented in `ARCHITECTURE.md` section 17. The runtime that reads it is `lib/sibling-artifacts.js` -(`detect`, `parsePlan`, `parseAudit`, `summarize`, `remediationTasks`, -`staleness`). It consumes godaudits 2.x computed score and coverage directly, +(`detect`, `parsePlan`, `validatePlanText`, `loadPlan`, `planExecutionState`, +`parseAudit`, `summarize`, `remediationTasks`, `staleness`). It consumes +godaudits 2.x computed score and coverage directly, recomputes counts only for legacy MDX, and never writes to sibling files. ## What Is Detected | System | Primary signals | Common files | Confidence | |---|---|---|---| -| godplans | `.godplans/`, `.godplans/PLAN.mdx` | `PLAN.mdx` | high (structured, single file) | +| godplans | `.godplans/`, `.godplans/PLAN.mdx` | `PLAN.mdx` plus executable `validate-plan.sh` | high (structured two-artifact contract; PLAN-only is legacy context) | | godaudits | `.godaudits/`, `.godaudits/AUDIT.json` | `AUDIT.json` plus generated `AUDIT.mdx` | high (structured canonical state) | | legacy planning | `.planning/`, `.legacy-planning/`, `LEGACY-PLANNING.md`, `legacy-planning*.md` | `PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, `STATE.md`, phase files | evidence-based | | BMAD | `_bmad/`, `_bmad-output/`, `.bmad-core/`, `.bmad/`, `BMAD.md` | `PRD.md`, `architecture.md`, epics, stories, `sprint-status.yaml`, `project-context.md` | evidence-based | @@ -55,17 +62,19 @@ recomputes counts only for legacy MDX, and never writes to sibling files. Detection is evidence-based for the foreign systems: marker folders raise confidence, but Godpowers also reads likely planning files and classifies them -by content. For the siblings, the canonical PLAN.mdx or AUDIT.json artifact is -itself high-confidence, so no import judgment agent is spawned. +by content. For the siblings, PLAN.mdx or canonical AUDIT.json is distinctive +enough for high-confidence detection, so no import judgment agent is spawned. +Detection confidence does not imply GP execution eligibility: `loadPlan` +separately enforces the two-artifact and lifecycle gates. ## Commands | You have | Run | Result | |---|---|---| -| `.godplans/PLAN.mdx` | `/god-migrate` | import the plan as tier seeds, `GP`/`R` ids preserved | +| `.godplans/PLAN.mdx` plus validator | `/god-migrate` | verify contract identity and structure, then import complete GP/R traceability as tier seeds | | `.godaudits/AUDIT.json` | `/god-migrate` then `/god-audit` | consume validated prior audit state instead of re-deriving it | | an open finding | `/god-fix GA-` | dispatch the remediation task with its evidence and `Verify` command as the done-check | -| an empty repo plus a `PLAN.mdx` | `/god-mode` | the greenfield-with-plan fast path: import the plan as tier-1 seeds, then execute | +| an empty repo plus a complete approved PLAN contract | `/god-mode` | import tier seeds, run the plan validator, then execute in wave order | `/god-init` auto-invokes the same import when it detects sibling or foreign context on cold start, and announces it in plain language before importing. @@ -83,13 +92,17 @@ lib/planning-systems.importPlanningContext(projectRoot, { The import writes: - `.godpowers/prep/IMPORTED-CONTEXT.mdx`, with dedicated sections for executable - plan signals (task counts recounted from checkboxes, open requirement domains, + plan signals (contract completeness, validator version, lifecycle status, + task and phase counts recounted from checkboxes, open requirement domains, open questions) and audit signals (overall score and verdict, per-domain scores and caps, coverage, check outcomes, evidence-record count, compliance, accepted risks, audit open questions, active findings by severity, and open `GA` count) -- missing Godpowers seed artifacts when source evidence exists (a `PLAN.mdx` - seeds PRD/ARCH/ROADMAP/STACK; an `AUDIT.json` seeds the harden tier) +- missing Godpowers seed artifacts when source evidence exists (a Godplans + contract seeds PRD/ARCH/ROADMAP/STACK/build state with complete task and + requirement traceability, writing the build ledger to + `.godpowers/prep/IMPORTED-BUILD-STATE.mdx` so the generated build STATE view + remains managed; an `AUDIT.json` seeds the harden tier) - `.godpowers/todos/TODOS.mdx`, when open GA tasks exist, with an idempotent managed section that preserves user-owned todos, maps Critical/High/Medium/Low findings to P0/P1/P2/P3, and carries the source GA id and Verify command @@ -116,13 +129,24 @@ Sync-back writes a managed companion file per system: Companions in the sibling `.godplans/`/`.godaudits/` directories are `.mdx` (the sibling family convention); companions in foreign directories stay `.md`. The -`PLAN.mdx`, `AUDIT.json`, and `AUDIT.mdx` are never modified by sync-back. +`PLAN.mdx`, `validate-plan.sh`, `AUDIT.json`, and `AUDIT.mdx` are never +modified by sync-back. ## Read-Only Boundary And Untrusted Commands The sibling files are read-only for Godpowers, with one exception: when Godpowers executes a plan or audit task, the executing agent follows the owning -product's executor rules. A godaudits 2.x completion updates reciprocal state +product's executor rules. Godpowers first requires `loadPlan` reason +`ready-for-validator`, which means a pinned regular executable companion, a +structurally valid plan, consistent lifecycle, and `approved` or `executing` +status. It then runs +`bash .godplans/validate-plan.sh .godplans/PLAN.mdx`; non-zero blocks all GP +work. The first executor moves `approved` to `executing`; each successful task +updates its checkbox, counters, date, and session log together. Scope drift +returns the plan to `planning`, preserves completed tasks, increments +`plan_version`, and requires fresh approval. A `done` plan is closed. + +A godaudits 2.x completion updates reciprocal state in `AUDIT.json`, runs `godaudits validate .godaudits/AUDIT.json --write`, and regenerates `AUDIT.mdx` plus SARIF when present. It never flips a checkbox in generated MDX. Every other flow writes only through `GODPOWERS-SYNC.mdx`. @@ -132,16 +156,18 @@ content. An agent may run one only when it is plainly read-only (a `grep`, `test`, `ls`, or `node --check` class command); anything that mutates state is shown to the user for confirmation first. -Authored JSON titles and commands are escaped before Godpowers writes MDX seed -or todo artifacts. Canonical audit reads allow up to 5 MiB so the full 414-check -ledger can be parsed instead of being truncated by the smaller foreign-file -sampling limit. Symlinked and other non-regular sibling artifacts are never -followed for parsing or remediation dispatch. +Authored plan and JSON content is escaped before Godpowers writes MDX seed or +todo artifacts. Canonical plan and audit reads allow up to 5 MiB so large plans +and the full 414-check ledger can be parsed instead of being truncated by the +smaller foreign-file sampling limit. Symlinked and other non-regular sibling +artifacts are never followed for parsing or task dispatch. ## Staleness -Imports record a content hash of the source file. `sibling-artifacts.staleness` -and `state.detectDrift` compare that hash against live `PLAN.mdx` or canonical +Imports record a fingerprint of the source contract. For Godplans 1.1, that +set is PLAN.mdx plus validate-plan.sh content and executable mode. +`sibling-artifacts.staleness` and +`state.detectDrift` compare that hash against the live contract or canonical `AUDIT.json` on every `/god-status`, `/god-doctor`, and `/god-context-scan`. Generated MDX refreshes do not count as audit drift. If you re-run godplans or godaudits mid-session, Godpowers surfaces a `[WARN]` @@ -151,8 +177,8 @@ of silently building on stale context. ## Auto-Spawning Most imports are local runtime work; no agent is needed when the evidence is -clear, and the siblings are always clear because they are structured single-file -artifacts. +clear, and the siblings are always distinctive because they have canonical +contract paths. Godpowers spawns `god-greenfieldifier` for a foreign-system import when one of these is true: @@ -169,12 +195,13 @@ artifact is rewritten. - Godpowers artifacts win over imported context. - User intent wins over imported context. -- A sibling `PLAN.mdx` is authored intent and outranks a reverse-engineered - reconstruction; when both exist, reconstruction only fills the gaps the plan - does not cover. +- A complete sibling Godplans contract is authored intent and outranks a + reverse-engineered reconstruction; when both exist, reconstruction only + fills the gaps the plan does not cover. Legacy or incomplete PLAN input stays + hypothesis-grade. - Pillars files under `agents/*.md` remain the native project context layer. -- Inferred claims stay `[HYPOTHESIS]` until confirmed; plan-stated facts are - `[DECISION]`-grade citations of the source `GP`/`R` id. +- Inferred claims stay `[HYPOTHESIS]` until confirmed; facts from a complete + contract are `[DECISION]`-grade citations of the source `GP`/`R` id. - Conflicts become `[OPEN QUESTION]` entries in prep or migration artifacts. ## Return Path diff --git a/docs/reference.md b/docs/reference.md index 503f6bc..bc2b14b 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -208,7 +208,7 @@ diff churn that cannot be traced to the request or slice plan. - `/god-review-changes` - Walk REVIEW-REQUIRED.mdx interactively. - `/god-reconcile` - Comprehensive reconciliation across all impacted artifacts. - `/god-reconstruct` - Reverse-engineer planning artifacts from existing code. -- `/god-migrate` - Convert adjacent planning-system context (including sibling `.godplans/PLAN.mdx` and canonical `.godaudits/AUDIT.json`) into Godpowers prep and seed artifacts, with open GA tasks synchronized to managed todos. +- `/god-migrate` - Convert adjacent planning-system context (including the Godplans 1.1 PLAN plus pinned validator contract and canonical `.godaudits/AUDIT.json`) into Godpowers prep and seed artifacts, with lifecycle-safe GP routing and open GA tasks synchronized to managed todos. ### Verification - `/god-lint` - Mechanical validation against have-nots catalog. diff --git a/lib/README.md b/lib/README.md index b0f08fd..d9058d0 100644 --- a/lib/README.md +++ b/lib/README.md @@ -84,8 +84,8 @@ package-level integrations. |--------|---------| | `context-writer.js` | Produce tool-specific context files. | | `context-budget.js` | Keep generated context within budget. | -| `planning-systems.js` | Detect and import legacy planning, BMAD, Superpowers, godplans, and godaudits context, including MDX-safe GA todo synchronization. | -| `sibling-artifacts.js` | Read-only parser/consumer for `.godplans/PLAN.mdx` and canonical `.godaudits/AUDIT.json`, with legacy MDX fallback: check and evidence ledgers, compliance, accepted risks, score and coverage digests, typed GA dispatch, and import staleness. | +| `planning-systems.js` | Detect and import legacy planning, BMAD, Superpowers, godplans, and godaudits context, including complete GP/R seed traceability and MDX-safe GA todo synchronization. | +| `sibling-artifacts.js` | Read-only consumer for the Godplans 1.1 PLAN plus pinned validator contract and canonical `.godaudits/AUDIT.json`, with lifecycle-safe GP routing, legacy context fallback, check and evidence ledgers, compliance, accepted risks, score and coverage digests, typed GA dispatch, and import staleness. | | `source-sync.js` | Write managed Godpowers progress back to source-system companion files. | | `design-detector.js` | Detect design-system conventions. | | `design-spec.js` | Normalize design specifications. | diff --git a/lib/feature-awareness.js b/lib/feature-awareness.js index 594955c..756cce3 100644 --- a/lib/feature-awareness.js +++ b/lib/feature-awareness.js @@ -158,8 +158,9 @@ function missingFeatureIds(current) { return expectedFeatureIds().filter((id) => !known.has(id)); } -// Sibling superskill artifacts (.godplans/PLAN.mdx, .godaudits/AUDIT.json) are -// structured single files; importing them needs no migration judgment, so +// Sibling superskill contracts (Godplans PLAN plus validator, and Godaudits +// AUDIT.json) use distinctive canonical paths; importing them needs no +// migration judgment, so // they never route to the low-confidence god-greenfieldifier spawn path. const SIBLING_SYSTEM_IDS = new Set(['godplans', 'godaudits']); diff --git a/lib/planning-systems.js b/lib/planning-systems.js index ea977e0..c7e9d14 100644 --- a/lib/planning-systems.js +++ b/lib/planning-systems.js @@ -1,8 +1,9 @@ /** * Planning System Migration * - * Detects legacy planning, BMAD, Superpowers, godplans, and godaudits project - * artifacts, converts their useful signals into Godpowers preparation + * Detects legacy planning, BMAD, Superpowers, the Godplans PLAN plus validator + * contract, and Godaudits project artifacts, converts their useful signals + * into Godpowers preparation * artifacts, and records source-system state so /god-sync can later write * progress back through lib/source-sync.js. */ @@ -21,7 +22,7 @@ const { } = require('./sync-fs'); const MAX_FILE_BYTES = 80 * 1024; -const MAX_CANONICAL_AUDIT_BYTES = 5 * 1024 * 1024; +const MAX_SIBLING_ARTIFACT_BYTES = 5 * 1024 * 1024; const MAX_SYSTEM_FILES = 80; const GODAUDITS_TODOS_PATH = '.godpowers/todos/TODOS.mdx'; const GODAUDITS_TODOS_BEGIN = ''; @@ -77,8 +78,8 @@ const GODPOWERS_ARTIFACTS = [ key: 'build', tier: 'tier-2', subStep: 'build', - artifact: 'build/STATE.mdx', - title: 'Imported Build State Seed', + artifact: 'prep/IMPORTED-BUILD-STATE.mdx', + title: 'Imported Build Plan And State Seed', sourceKinds: ['plan', 'summary', 'verification', 'review', 'uat', 'sprint'] }, { @@ -152,7 +153,11 @@ function hashFiles(projectRoot, files) { for (const file of files.map((f) => f.path).sort()) { const full = path.join(projectRoot, file); h.update(file); - if (fs.existsSync(full) && fs.lstatSync(full).isFile()) { + const stat = fs.existsSync(full) ? fs.lstatSync(full) : null; + if (file === siblingArtifacts.PLAN_VALIDATOR_PATH) { + h.update(`mode:${stat && stat.isFile() ? stat.mode & 0o111 : 'non-regular'}`); + } + if (stat && stat.isFile()) { const fd = fs.openSync(full, READ_NOFOLLOW); const chunk = Buffer.allocUnsafe(64 * 1024); try { @@ -179,8 +184,12 @@ function readText(projectRoot, relPath) { const stat = fs.lstatSync(full); if (!stat.isFile()) return ''; const size = stat.size; - const maxBytes = relPath === siblingArtifacts.AUDIT_JSON_PATH - ? MAX_CANONICAL_AUDIT_BYTES + const maxBytes = [ + siblingArtifacts.PLAN_PATH, + siblingArtifacts.PLAN_VALIDATOR_PATH, + siblingArtifacts.AUDIT_JSON_PATH + ].includes(relPath) + ? MAX_SIBLING_ARTIFACT_BYTES : MAX_FILE_BYTES; const length = Math.min(size, maxBytes); const buffer = Buffer.allocUnsafe(length); @@ -250,6 +259,8 @@ function classifyFile(filePath, content) { const body = `${lower}\n${content.slice(0, 3000).toLowerCase()}`; const kinds = new Set(); + if (filePath === siblingArtifacts.PLAN_VALIDATOR_PATH) return ['validator']; + // Sibling superskill id grammars are case-sensitive, so they match against // the raw content rather than the lowercased keyword body. if (/\bGP-\d{3,}\b/.test(content) || RE_PLAN_REQUIREMENT_ID.test(content)) { @@ -364,6 +375,12 @@ function detectSystem(projectRoot, id, system) { for (const file of standalone) files.push(file); let unique = [...new Set(files)].slice(0, MAX_SYSTEM_FILES); + if (id === 'godplans' && exists(projectRoot, siblingArtifacts.PLAN_PATH)) { + unique = [siblingArtifacts.PLAN_PATH]; + if (exists(projectRoot, siblingArtifacts.PLAN_VALIDATOR_PATH)) { + unique.push(siblingArtifacts.PLAN_VALIDATOR_PATH); + } + } if (id === 'godaudits') { const canonical = [ siblingArtifacts.AUDIT_JSON_PATH, @@ -374,6 +391,7 @@ function detectSystem(projectRoot, id, system) { } if (markerHits.length === 0 && unique.length === 0) return null; + const loadedPlan = id === 'godplans' ? siblingArtifacts.loadPlan(projectRoot) : null; const fileRecords = unique.map((file) => { const content = readText(projectRoot, file); const canonicalAudit = id === 'godaudits' && file === siblingArtifacts.AUDIT_JSON_PATH @@ -391,7 +409,13 @@ function detectSystem(projectRoot, id, system) { kinds, signals: canonicalAudit && canonicalAudit.parseError ? [] : extractSignals(content), bytes: Buffer.byteLength(content), - parseError: canonicalAudit ? canonicalAudit.parseError : null + parseError: canonicalAudit + ? canonicalAudit.parseError + : (file === siblingArtifacts.PLAN_PATH && loadedPlan && !loadedPlan.contract.complete + ? loadedPlan.contract.reason + : null), + plan: file === siblingArtifacts.PLAN_PATH && loadedPlan ? loadedPlan.plan : null, + planContract: file === siblingArtifacts.PLAN_PATH && loadedPlan ? loadedPlan.contract : null }; }); @@ -407,7 +431,8 @@ function detectSystem(projectRoot, id, system) { confidence, markers: markerHits.sort(), files: fileRecords, - importHash: hashFiles(projectRoot, fileRecords) + importHash: hashFiles(projectRoot, fileRecords), + planContract: loadedPlan ? loadedPlan.contract : null }; } @@ -428,16 +453,21 @@ function filesForKinds(system, kinds) { return system.files.filter((file) => file.kinds.some((kind) => wanted.has(kind))); } -// Sibling superskill files are authored, machine-checked artifacts, so -// plan-stated facts they carry (task lines, requirement ids, headings) are -// [DECISION]-grade; claims inferred from them stay [HYPOTHESIS]. -function signalGrade(systemId) { +// Canonical Godaudits state and complete Godplans 1.1 two-file emissions are +// machine-checked authored state. Legacy or incomplete plans remain useful +// migration context, but their signals stay hypotheses until repaired. +function signalGrade(systemId, file = null) { + if (systemId === 'godplans') { + return file && file.planContract && file.planContract.complete + ? '[DECISION]' + : '[HYPOTHESIS]'; + } return SIBLING_SYSTEM_IDS.has(systemId) ? '[DECISION]' : '[HYPOTHESIS]'; } function buildSourceLine(system, file) { const label = file.signals[0] ? `, signal: ${safeImportedText(file.signals[0], 1500)}` : ''; - return `- ${signalGrade(system.id)} ${safeImportedText(system.name)} source ${safeImportedText(file.path)} maps to ${file.kinds.join(', ')}${label}.`; + return `- ${signalGrade(system.id, file)} ${safeImportedText(system.name)} source ${safeImportedText(file.path)} maps to ${file.kinds.join(', ')}${label}.`; } function buildImportedContext(detection, projectRoot) { @@ -530,8 +560,48 @@ const SIBLING_ID_SIGNAL_RE = /\b(?:GP|GA)-\d+\b|\b[RAF]-[A-Z]+-\d+\b|\bR-\d+\.\d // Sibling files are heading-heavy, so a plain slice(0, 3) would drop the // id-bearing task lines that seeds must preserve verbatim; float those to // the front and allow a slightly deeper slice for sibling systems. -function seedSignals(system, file) { +function formatPlanTaskSignal(task) { + const fields = [ + `${task.id} [${task.status}] [W${task.wave}] ${task.title}`, + `Reuses: ${task.reuses || 'not recorded'}`, + `Verify: ${task.verify || 'not recorded'}`, + `Requirements: ${task.requirements.length ? task.requirements.join(', ') : 'none'}` + ]; + return fields.join('; '); +} + +function structuredGodplansSeedSignals(file, artifact) { + const plan = file.plan; + if (!plan) return []; + if (artifact.key === 'prd') { + const definitions = plan.requirementDefinitions.map((requirement) => ( + `${requirement.id}: ${requirement.text}` + )); + const domainIds = plan.requirementIds.domain.map((id) => `Plan requirement catalog reference: ${id}`); + return [...definitions, ...domainIds, ...plan.tasks.map(formatPlanTaskSignal)]; + } + if (artifact.key === 'arch') { + return plan.tasks + .filter((task) => task.requirements.some((id) => /^R-ARCH-/.test(id))) + .map(formatPlanTaskSignal); + } + if (artifact.key === 'stack') { + return plan.tasks + .filter((task) => task.requirements.some((id) => /^R-STACK-/.test(id))) + .map(formatPlanTaskSignal); + } + if (artifact.key === 'roadmap' || artifact.key === 'build') { + return plan.tasks.map(formatPlanTaskSignal); + } + return []; +} + +function seedSignals(system, file, artifact) { if (!SIBLING_SYSTEM_IDS.has(system.id)) return file.signals.slice(0, 3); + if (system.id === 'godplans') { + const structured = structuredGodplansSeedSignals(file, artifact); + if (structured.length > 0) return structured; + } const idBearing = file.signals.filter((signal) => SIBLING_ID_SIGNAL_RE.test(signal)); const rest = file.signals.filter((signal) => !SIBLING_ID_SIGNAL_RE.test(signal)); return [...idBearing, ...rest].slice(0, 6); @@ -550,12 +620,12 @@ function buildSeedArtifact(detection, artifact) { let count = 0; for (const system of detection.systems) { - const grade = signalGrade(system.id); for (const file of filesForKinds(system, artifact.sourceKinds).slice(0, 12)) { + const grade = signalGrade(system.id, file); lines.push(`- [HYPOTHESIS] ${safeImportedText(system.name)} source ${safeImportedText(file.path)} may inform this artifact.`); // Preserve GP-/GA-/R- ids for linkage while escaping authored text for // safe embedding in a generated MDX artifact. - for (const signal of seedSignals(system, file)) { + for (const signal of seedSignals(system, file, artifact)) { lines.push(`- ${grade} Imported signal from ${safeImportedText(file.path)}: ${safeImportedText(signal, 1500)}.`); } count += 1; diff --git a/lib/sibling-artifacts.js b/lib/sibling-artifacts.js index a47ba50..838892f 100644 --- a/lib/sibling-artifacts.js +++ b/lib/sibling-artifacts.js @@ -2,8 +2,9 @@ * Sibling Superskill Artifacts * * Read-only parser/consumer for the two sibling superskill artifacts: - * `.godplans/PLAN.mdx` (godplans master plan) and `.godaudits/AUDIT.json` - * (godaudits 2.x canonical state). A generated `.godaudits/AUDIT.mdx` report + * `.godplans/PLAN.mdx` plus its pinned `validate-plan.sh` companion (Godplans + * 1.1 master-plan contract), and `.godaudits/AUDIT.json` (godaudits 2.x + * canonical state). A generated `.godaudits/AUDIT.mdx` report * and legacy 1.x MDX audits remain readable fallbacks. JSON computed state is * consumed directly; legacy MDX counts are recomputed from the body. * This module never writes to disk; import and sync-back stay in @@ -18,6 +19,7 @@ const frontmatterHelper = require('./frontmatter'); const { exists, resolveArtifact } = require('./sync-fs'); const PLAN_PATH = '.godplans/PLAN.mdx'; +const PLAN_VALIDATOR_PATH = '.godplans/validate-plan.sh'; const AUDIT_JSON_PATH = '.godaudits/AUDIT.json'; const AUDIT_REPORT_PATH = '.godaudits/AUDIT.mdx'; const AUDIT_LEGACY_PATH = '.godaudits/AUDIT.md'; @@ -25,6 +27,14 @@ const AUDIT_PATH = AUDIT_JSON_PATH; const MAX_SIBLING_FILE_BYTES = 5 * 1024 * 1024; const READ_NOFOLLOW = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0); +// Godplans 1.1.0 requires this exact self-contained validator beside every +// emitted plan. Unknown hashes fail closed for GP execution while remaining +// readable as migration context, so a future godplans validator update cannot +// silently weaken the execution gate. +const GODPLANS_VALIDATOR_HASHES = Object.freeze({ + 'sha256:cec8691bb32f272bfe29acdab435be6f95d55405a914fc6ff33277aca5c8eb6b': '1.1.0' +}); + // The 18 domain codes shared by R--n plan requirements, A--n audit // checks, and F--n findings. A-n mirrors R-n one to one by design. const DOMAIN_CODES = [ @@ -42,6 +52,34 @@ const FINDING_HEAD_RE = /^#### (F-[A-Z]+-\d+) (.+?) \[(Critical|High|Medium|Low) const FINDING_FIELD_RE = /^- (Where|Evidence|Impact|Fix|Verify the fix|Checks|Status|Remediation|Resolved): (.+)$/; const STORY_REQUIREMENT_RE = /\bR-\d+\.\d+\b/g; const DOMAIN_REQUIREMENT_RE = new RegExp(`\\bR-(?:${DOMAIN_ALTERNATION})-\\d+\\b`, 'g'); +const PLAN_STATUS_VALUES = new Set(['planning', 'approved', 'executing', 'done']); +const PLAN_MODE_VALUES = new Set(['greenfield', 'brownfield', 'replan']); +const PLAN_REQUIRED_FIELDS = ['Files', 'Depends on', 'Reuses', 'Acceptance', 'Verify', 'Requirements']; +const PLAN_REQUIRED_FRONTMATTER = [ + 'name', 'plan_version', 'status', 'created', 'updated', 'mode', 'archetype', + 'domains_applicable', 'domains_excluded' +]; +const PLAN_REQUIREMENT_CATALOG_MAX = Object.freeze({ + ARCH: 19, + BUILD: 20, + CODE: 22, + DB: 22, + DEPLOY: 18, + DNA: 20, + LAUNCH: 21, + LLM: 23, + MEM: 18, + OBS: 20, + PRD: 17, + REPO: 20, + ROAD: 20, + SEC: 25, + SEO: 22, + STACK: 20, + UI: 20, + UX: 20 +}); +const PLAN_BANNED_UNICODE_RE = /[\u2013\u2014\u2018-\u201f\u2026\u2190-\u21ff\u2500-\u257f\ufe0f\u{1f000}-\u{1faff}]/u; function normalize(text) { return String(text || '').replace(/\r\n/g, '\n'); @@ -220,7 +258,9 @@ function sectionListItems(text, heading) { const items = []; for (let i = start + 1; i < lines.length; i++) { if (/^## /.test(lines[i])) break; - const item = lines[i].match(/^- (.+)$/) || lines[i].match(/^\d+\. (.+)$/); + const item = lines[i].match(/^- (.+)$/) || + lines[i].match(/^\d+\. (.+)$/) || + lines[i].match(/^### (Q\d+:.+)$/); if (item) items.push(item[1].trim()); } return items; @@ -245,6 +285,265 @@ function requirementDomain(id) { return m ? m[1] : null; } +function parsePlanFrontmatterState(lines) { + const fields = {}; + const fieldCounts = {}; + const counters = {}; + const counterCounts = {}; + let end = -1; + if (lines[0] === '---') { + end = lines.findIndex((line, index) => index > 0 && line === '---'); + } + if (end < 0) return { end, fields, fieldCounts, counters, counterCounts }; + + for (let i = 1; i < end; i++) { + let match = lines[i].match(/^([a-z_]+):(?:[ \t]*(.*))?$/); + if (match) { + fieldCounts[match[1]] = (fieldCounts[match[1]] || 0) + 1; + fields[match[1]] = String(match[2] || '').trim(); + continue; + } + match = lines[i].match(/^ (phases_total|phases_done|tasks_total|tasks_done):[ \t]*(.*)$/); + if (match) { + counterCounts[match[1]] = (counterCounts[match[1]] || 0) + 1; + counters[match[1]] = String(match[2] || '').trim(); + } + } + return { end, fields, fieldCounts, counters, counterCounts }; +} + +function planRequirementCatalog() { + const ids = new Set(); + for (const [prefix, max] of Object.entries(PLAN_REQUIREMENT_CATALOG_MAX)) { + for (let number = 1; number <= max; number++) ids.add(`R-${prefix}-${number}`); + } + return ids; +} + +/** + * Static, non-executing preflight for the Godplans 1.1.0 PLAN contract. + * The sibling validator remains the authoritative execution gate. This + * mirror lets import, doctor, and routing fail closed without automatically + * executing repository-owned shell code. + */ +function validatePlanText(text, opts = {}) { + const source = normalize(text); + const lines = source.split('\n').map((line) => line.replace(/\r$/, '')); + const errors = []; + const fail = (message) => errors.push(message); + const raw = parsePlanFrontmatterState(lines); + const allowPlanning = opts.allowPlanning !== false; + + if (lines[0] !== '---') fail('frontmatter must begin on line 1 with ---'); + else if (raw.end < 0) fail('frontmatter is missing its closing ---'); + + for (const key of PLAN_REQUIRED_FRONTMATTER) { + if (!(key in raw.fields)) fail(`missing frontmatter field: ${key}`); + else if (!raw.fields[key] && key !== 'domains_excluded') fail(`frontmatter field is empty: ${key}`); + if ((raw.fieldCounts[key] || 0) > 1) fail(`duplicate frontmatter field: ${key}`); + } + if (!('progress' in raw.fields)) fail('missing frontmatter field: progress'); + if ((raw.fieldCounts.progress || 0) > 1) fail('duplicate frontmatter field: progress'); + + if ('plan_version' in raw.fields && !/^[1-9]\d*$/.test(raw.fields.plan_version)) { + fail(`plan_version must be a positive integer, found '${raw.fields.plan_version}'`); + } + const status = raw.fields.status || null; + if (status && !PLAN_STATUS_VALUES.has(status)) { + fail(`invalid status '${status}'; expected planning, approved, executing, or done`); + } else if (!allowPlanning && status && status !== 'approved' && status !== 'executing') { + fail(`execution requires status approved or executing, found '${status}'`); + } + if (raw.fields.mode && !PLAN_MODE_VALUES.has(raw.fields.mode)) { + fail(`invalid mode '${raw.fields.mode}'; expected greenfield, brownfield, or replan`); + } + for (const key of ['created', 'updated']) { + if (raw.fields[key] && !/^\d{4}-\d{2}-\d{2}$/.test(raw.fields[key])) { + fail(`${key} must use YYYY-MM-DD, found '${raw.fields[key]}'`); + } + } + for (const key of ['phases_total', 'phases_done', 'tasks_total', 'tasks_done']) { + if (!(key in raw.counters)) fail(`missing progress counter: ${key}`); + else if (!/^\d+$/.test(raw.counters[key])) { + fail(`progress counter ${key} must be a non-negative integer, found '${raw.counters[key]}'`); + } + if ((raw.counterCounts[key] || 0) > 1) fail(`duplicate progress counter: ${key}`); + } + + const localRequirements = new Set(); + let inRequirements = false; + for (const line of lines) { + if (line === '## Requirements') { + inRequirements = true; + continue; + } + if (inRequirements && /^## /.test(line)) inRequirements = false; + if (!inRequirements) continue; + const prose = line.match(/^(R-\d+\.\d+):/); + const table = line.match(/^\|\s*(R-\d+\.\d+)\s*\|/); + if (prose || table) localRequirements.add((prose || table)[1]); + } + const catalog = planRequirementCatalog(); + const phases = []; + const tasks = []; + const definitions = new Map(); + let currentPhase = -1; + + for (let i = 0; i < lines.length; i++) { + const phase = lines[i].match(/^## Phase ([1-9]\d*):\s*(.+?)\s*$/); + if (phase) { + phases.push({ number: Number(phase[1]), name: phase[2], line: i + 1, taskIndexes: [] }); + currentPhase = phases.length - 1; + continue; + } + + const head = lines[i].match(/^- \[([ x])\] (GP-[1-9]\d{2,})\b/); + if (head) { + const full = lines[i].match(/^- \[([ x])\] (GP-[1-9]\d{2,}) (?:\[P\] )?\[W([1-9]\d*)\.([1-9]\d*)\] \S/); + const task = { + id: head[2], + done: head[1] === 'x', + line: i + 1, + phaseIndex: currentPhase, + wavePhase: full ? Number(full[3]) : null, + fields: {} + }; + if (!full) fail(`${task.id} has malformed task heading`); + if (currentPhase < 0) fail(`${task.id} is not inside a numbered phase`); + else { + phases[currentPhase].taskIndexes.push(tasks.length); + if (task.wavePhase !== null && task.wavePhase !== phases[currentPhase].number) { + fail(`${task.id} wave phase ${task.wavePhase} does not match Phase ${phases[currentPhase].number}`); + } + } + if (definitions.has(task.id)) { + fail(`duplicate task definition ID ${task.id} on lines ${definitions.get(task.id)} and ${i + 1}`); + } else { + definitions.set(task.id, i + 1); + } + for (let j = i + 1; j < lines.length; j++) { + if (/^- \[[ x]\] GP-/.test(lines[j]) || /^## Phase [1-9]\d*:/.test(lines[j])) break; + const field = lines[j].match(/^ - (Files|Depends on|Reuses|Acceptance|Verify|Requirements):[ \t]*(.*)$/); + if (!field) continue; + if (!task.fields[field[1]]) task.fields[field[1]] = []; + task.fields[field[1]].push(field[2].trim()); + } + tasks.push(task); + } else if (/^- \[[^\]]*\] GP-/.test(lines[i])) { + fail(`malformed task definition on line ${i + 1}`); + } + } + + phases.forEach((phase, index) => { + const expected = index + 1; + if (phase.number !== expected) { + fail(`phase numbers must be sequential: expected Phase ${expected}, found Phase ${phase.number}`); + } + }); + + for (const task of tasks) { + for (const field of PLAN_REQUIRED_FIELDS) { + const values = task.fields[field] || []; + if (values.length === 0) fail(`${task.id} missing required field: ${field}`); + else if (values.length > 1) fail(`${task.id} has duplicate required field: ${field}`); + else if (!values[0]) fail(`${task.id} has empty required field: ${field}`); + } + const dependencyValue = (task.fields['Depends on'] || [])[0]; + if (dependencyValue && dependencyValue !== 'none') { + const dependencies = dependencyValue.split(/\s*,\s*/); + if (dependencies.length === 0 || dependencies.some((id) => !/^GP-[1-9]\d{2,}$/.test(id))) { + fail(`${task.id} has malformed Depends on value '${dependencyValue}'`); + } else { + for (const dependency of dependencies) { + if (dependency === task.id) fail(`${task.id} depends on itself`); + else if (!definitions.has(dependency)) fail(`${task.id} depends on undefined task ${dependency}`); + else if (definitions.get(dependency) > task.line) fail(`${task.id} depends on later task ${dependency}`); + } + } + } + const requirementValue = (task.fields.Requirements || [])[0]; + if (requirementValue) { + const requirements = requirementValue.split(/\s*,\s*/); + if (requirements.length === 0 || requirements.some((id) => !/^R-(?:\d+\.\d+|[A-Z][A-Z0-9-]*-\d+)$/.test(id))) { + fail(`${task.id} has malformed Requirements value '${requirementValue}'`); + } else { + for (const id of requirements) { + if (!localRequirements.has(id) && !catalog.has(id)) fail(`${task.id} cites undefined requirement ${id}`); + } + } + } + } + + let phasesDone = 0; + for (const phase of phases) { + if (phase.taskIndexes.length === 0) { + fail(`Phase ${phase.number} has no task definitions`); + continue; + } + if (phase.taskIndexes.every((index) => tasks[index].done)) phasesDone += 1; + } + const derived = { + phases_total: phases.length, + phases_done: phasesDone, + tasks_total: tasks.length, + tasks_done: tasks.filter((task) => task.done).length + }; + for (const [key, value] of Object.entries(derived)) { + if (/^\d+$/.test(raw.counters[key] || '') && Number(raw.counters[key]) !== value) { + fail(`${key} is ${raw.counters[key]}, derived value is ${value}`); + } + } + + const openQuestionsCount = lines.filter((line) => line === '## Open Questions').length; + if (openQuestionsCount !== 1) { + fail(`expected exactly one ## Open Questions section, found ${openQuestionsCount}`); + } + const finalPhase = phases.length ? phases[phases.length - 1].name : 'none'; + if (finalPhase !== 'Verification') fail(`final phase must be Verification, found '${finalPhase}'`); + lines.forEach((line, index) => { + if (PLAN_BANNED_UNICODE_RE.test(line)) fail(`banned Unicode on line ${index + 1}`); + }); + + return { + valid: errors.length === 0, + errors, + status, + mode: raw.fields.mode || null, + planVersion: /^\d+$/.test(raw.fields.plan_version || '') ? Number(raw.fields.plan_version) : null, + counters: raw.counters, + derived, + phases: phases.map((phase) => ({ + number: phase.number, + name: phase.name, + line: phase.line, + tasksTotal: phase.taskIndexes.length, + tasksDone: phase.taskIndexes.filter((index) => tasks[index].done).length + })), + localRequirements: [...localRequirements] + }; +} + +function parseRequirementDefinitions(text) { + const definitions = []; + let inRequirements = false; + for (const line of normalize(text).split('\n')) { + if (line === '## Requirements') { + inRequirements = true; + continue; + } + if (inRequirements && /^## /.test(line)) inRequirements = false; + if (!inRequirements) continue; + let match = line.match(/^(R-\d+\.\d+):\s*(.+)$/); + if (match) { + definitions.push({ id: match[1], text: match[2].trim() }); + continue; + } + match = line.match(/^\|\s*(R-\d+\.\d+)\s*\|\s*(.*?)\s*\|/); + if (match) definitions.push({ id: match[1], text: match[2].trim() }); + } + return definitions; +} + /** * Parse `.godplans/PLAN.mdx` text. Tolerates missing frontmatter and missing * sections; counts are recomputed from checkboxes, never read from the @@ -255,6 +554,12 @@ function parsePlan(text) { const frontmatter = readFrontmatter(source); const { tasks, superseded } = parseTasks(source, PLAN_TASK_RE); const counts = countTasks(tasks); + const validation = validatePlanText(source, { allowPlanning: true }); + const requirementDefinitions = parseRequirementDefinitions(source); + const lifecycleErrors = []; + if (validation.status === 'done' && counts.open > 0) { + lifecycleErrors.push(`done plan still has ${counts.open} open task(s)`); + } const requirementIds = { story: matchAll(source, STORY_REQUIREMENT_RE), @@ -275,7 +580,18 @@ function parsePlan(text) { tasks, superseded, counts, + phases: validation.phases, + validation, + lifecycle: { + status: validation.status, + requiresApproval: validation.status === 'planning', + executionStatus: validation.status === 'approved' || validation.status === 'executing', + closed: validation.status === 'done', + consistent: lifecycleErrors.length === 0, + errors: lifecycleErrors + }, requirementIds, + requirementDefinitions, openRequirementDomains: [...openDomains].sort(), openQuestions: sectionListItems(source, 'Open Questions') }; @@ -595,15 +911,20 @@ function parseAudit(text, sourcePath = null) { /** * Hash project-relative paths the same way lib/planning-systems.js hashes an - * imported system (path names plus file contents, sorted), so staleness can - * compare against the recorded source-systems import hash byte for byte. + * imported system (sorted path names and file contents, plus executable bits + * for the Godplans validator), so staleness can compare against the recorded + * source-systems import hash byte for byte. */ function hashPaths(projectRoot, relPaths) { const h = crypto.createHash('sha256'); for (const relPath of [...relPaths].sort()) { h.update(relPath); const full = path.join(projectRoot, relPath); - if (fs.existsSync(full) && fs.lstatSync(full).isFile()) { + const stat = fs.existsSync(full) ? fs.lstatSync(full) : null; + if (relPath === PLAN_VALIDATOR_PATH) { + h.update(`mode:${stat && stat.isFile() ? stat.mode & 0o111 : 'non-regular'}`); + } + if (stat && stat.isFile()) { const fd = fs.openSync(full, READ_NOFOLLOW); const chunk = Buffer.allocUnsafe(64 * 1024); try { @@ -620,7 +941,7 @@ function hashPaths(projectRoot, relPaths) { return `sha256:${h.digest('hex')}`; } -function readRegularFileOrNull(projectRoot, relPath) { +function readRegularBufferOrNull(projectRoot, relPath) { const full = path.join(projectRoot, relPath); if (!fs.existsSync(full)) return null; try { @@ -639,12 +960,144 @@ function readRegularFileOrNull(projectRoot, relPath) { } finally { fs.closeSync(fd); } - return buffer.subarray(0, bytesRead).toString('utf8'); + return buffer.subarray(0, bytesRead); } catch (err) { return null; } } +function readRegularFileOrNull(projectRoot, relPath) { + const buffer = readRegularBufferOrNull(projectRoot, relPath); + return buffer === null ? null : buffer.toString('utf8'); +} + +function inspectSiblingFile(projectRoot, relPath) { + const full = path.join(projectRoot, relPath); + if (!fs.existsSync(full)) { + return { path: relPath, present: false, regular: false, executable: false, hash: null }; + } + try { + const stat = fs.lstatSync(full); + const regular = stat.isFile() && !stat.isSymbolicLink(); + const buffer = regular ? readRegularBufferOrNull(projectRoot, relPath) : null; + return { + path: relPath, + present: true, + regular: regular && buffer !== null, + executable: regular && (stat.mode & 0o111) !== 0, + hash: buffer === null + ? null + : `sha256:${crypto.createHash('sha256').update(buffer).digest('hex')}` + }; + } catch (err) { + return { path: relPath, present: true, regular: false, executable: false, hash: null }; + } +} + +function planContractReason(plan, validator, validatorVersion) { + if (!plan.validation.valid) return 'plan-structure-invalid'; + if (!plan.lifecycle.consistent) return 'lifecycle-inconsistent'; + if (!validator.present) return 'validator-missing'; + if (!validator.regular) return 'validator-unreadable-or-non-regular'; + if (!validator.executable) return 'validator-not-executable'; + if (!validatorVersion) return 'validator-unsupported'; + if (plan.lifecycle.requiresApproval) return 'awaiting-explicit-approval'; + if (plan.lifecycle.closed) return 'plan-closed'; + if (!plan.lifecycle.executionStatus) return 'lifecycle-invalid'; + return 'ready-for-validator'; +} + +/** + * Load both files in the Godplans 1.1.0 two-artifact contract without running + * repository-owned shell. Callers must run validatorCommand immediately + * before GP execution when executionEligible is true. + */ +function loadPlan(projectRoot, opts = {}) { + const planInfo = inspectSiblingFile(projectRoot, PLAN_PATH); + if (!planInfo.present) return null; + const planText = readRegularFileOrNull(projectRoot, PLAN_PATH); + if (!planInfo.regular || planText === null) { + return { + path: PLAN_PATH, + validatorPath: PLAN_VALIDATOR_PATH, + plan: parsePlan(''), + contract: { + complete: false, + executionEligible: false, + reason: 'plan-unreadable-or-non-regular', + validatorVersion: null, + validator: inspectSiblingFile(projectRoot, PLAN_VALIDATOR_PATH), + validatorCommand: `bash ${PLAN_VALIDATOR_PATH} ${PLAN_PATH}` + } + }; + } + + const plan = parsePlan(planText); + const validator = inspectSiblingFile(projectRoot, PLAN_VALIDATOR_PATH); + const trusted = opts.trustedValidatorHashes || GODPLANS_VALIDATOR_HASHES; + const validatorVersion = validator.hash && trusted[validator.hash] + ? trusted[validator.hash] + : null; + const complete = plan.validation.valid && plan.lifecycle.consistent && validator.present && validator.regular && + validator.executable && Boolean(validatorVersion); + const reason = planContractReason(plan, validator, validatorVersion); + return { + path: PLAN_PATH, + validatorPath: PLAN_VALIDATOR_PATH, + plan, + contract: { + complete, + executionEligible: complete && plan.lifecycle.executionStatus, + reason, + validatorVersion, + validator, + validatorCommand: `bash ${PLAN_VALIDATOR_PATH} ${PLAN_PATH}`, + structuralCommand: `bash ${PLAN_VALIDATOR_PATH} --allow-planning ${PLAN_PATH}` + } + }; +} + +function planExecutionState(projectRoot, opts = {}) { + const loaded = loadPlan(projectRoot, opts); + if (!loaded) { + return { + present: false, + executionEligible: false, + reason: 'plan-missing', + validatorCommand: null, + nextTasks: [] + }; + } + const done = new Set(loaded.plan.tasks.filter((task) => task.done).map((task) => task.id)); + const openTasks = loaded.plan.tasks.filter((task) => !task.done); + let nextTasks = []; + if (loaded.contract.executionEligible && openTasks.length > 0) { + const first = openTasks[0]; + if (first.dependsOn.every((dependency) => done.has(dependency))) { + nextTasks = [first]; + if (first.parallel) { + const claimedFiles = new Set(first.files); + for (const candidate of openTasks.slice(1)) { + if (candidate.wave !== first.wave || !candidate.parallel) break; + if (!candidate.dependsOn.every((dependency) => done.has(dependency))) continue; + if (candidate.files.some((file) => claimedFiles.has(file))) continue; + nextTasks.push(candidate); + candidate.files.forEach((file) => claimedFiles.add(file)); + } + } + } + } + return { + present: true, + executionEligible: loaded.contract.executionEligible, + reason: loaded.contract.reason, + status: loaded.plan.lifecycle.status, + validatorCommand: loaded.contract.validatorCommand, + nextTasks, + openTasks: openTasks.length + }; +} + function detectOne(projectRoot, canonicalPath) { const resolved = resolveArtifact(projectRoot, canonicalPath); const full = path.join(projectRoot, resolved); @@ -704,32 +1157,48 @@ function detect(projectRoot) { const auditPath = resolveAuditSource(projectRoot); return { plan: detectOne(projectRoot, PLAN_PATH), + planValidator: detectOne(projectRoot, PLAN_VALIDATOR_PATH), audit: exists(projectRoot, auditPath) ? detectOne(projectRoot, auditPath) : null }; } /** - * Plain-text digest block suitable for IMPORTED-CONTEXT. Plan-stated facts - * are [DECISION]-grade (the plan and audit are authored, machine-checked - * artifacts); absence lines stay [HYPOTHESIS]. + * Plain-text digest block suitable for IMPORTED-CONTEXT. Facts from complete + * Godplans contracts and canonical audits are [DECISION]-grade; legacy plan + * signals and absence lines stay [HYPOTHESIS]. */ function summarize(projectRoot) { const lines = []; lines.push('## Executable Plan Signals'); lines.push(''); - const resolvedPlan = resolveArtifact(projectRoot, PLAN_PATH); - const planText = readRegularFileOrNull(projectRoot, resolvedPlan); - if (planText === null) { + const loadedPlan = loadPlan(projectRoot); + if (loadedPlan === null) { lines.push('- [HYPOTHESIS] No godplans master plan was detected.'); } else { - const plan = parsePlan(planText); + const plan = loadedPlan.plan; const fm = plan.frontmatter || {}; - lines.push(`- [DECISION] godplans master plan detected at ${resolvedPlan}.`); - lines.push(`- [DECISION] Plan mode: ${fm.mode || 'unknown'}. Archetype: ${fm.archetype || 'unknown'}. Status: ${fm.status || 'unknown'}.`); - lines.push(`- [DECISION] Plan tasks recounted from checkboxes: ${plan.counts.total} total, ${plan.counts.done} done, ${plan.counts.open} open.`); - lines.push(`- [DECISION] Open requirement domains: ${plan.openRequirementDomains.length ? plan.openRequirementDomains.join(', ') : 'none'}.`); - lines.push(`- [DECISION] Open questions: ${plan.openQuestions.length}.`); + const authoredGrade = loadedPlan.contract.complete ? '[DECISION]' : '[HYPOTHESIS]'; + lines.push(`- [DECISION] godplans master plan detected at ${loadedPlan.path}.`); + lines.push(`- ${authoredGrade} Plan mode: ${fm.mode || 'unknown'}. Archetype: ${fm.archetype || 'unknown'}. Status: ${fm.status || 'unknown'}.`); + lines.push(`- ${authoredGrade} Plan tasks recounted from checkboxes: ${plan.counts.total} total, ${plan.counts.done} done, ${plan.counts.open} open.`); + lines.push(`- ${authoredGrade} Open requirement domains: ${plan.openRequirementDomains.length ? plan.openRequirementDomains.join(', ') : 'none'}.`); + lines.push(`- ${authoredGrade} Open questions: ${plan.openQuestions.length}.`); + if (loadedPlan.contract.complete) { + lines.push(`- [DECISION] Godplans ${loadedPlan.contract.validatorVersion} two-artifact contract is complete: the validator is trusted, regular, executable, and the plan passes static structural preflight.`); + } else { + lines.push(`- [DECISION] Godplans execution is blocked: ${loadedPlan.contract.reason}. Treat GP tasks as read-only migration context until the contract is repaired.`); + if (plan.validation.errors.length > 0) { + lines.push(`- [DECISION] Plan structural preflight found ${plan.validation.errors.length} error(s); first error: ${plan.validation.errors[0]}.`); + } + } + if (loadedPlan.contract.executionEligible) { + lines.push(`- [DECISION] GP execution status is eligible, but ${loadedPlan.contract.validatorCommand} must pass immediately before work starts.`); + } else if (plan.lifecycle.requiresApproval) { + lines.push('- [DECISION] GP execution awaits explicit user approval; Godpowers must not change planning to approved on the user\'s behalf.'); + } else if (plan.lifecycle.closed) { + lines.push('- [DECISION] The plan is closed; no GP task may be dispatched.'); + } } lines.push(''); @@ -838,13 +1307,19 @@ function staleness(projectRoot, stateData) { module.exports = { PLAN_PATH, + PLAN_VALIDATOR_PATH, AUDIT_PATH, AUDIT_JSON_PATH, AUDIT_REPORT_PATH, AUDIT_LEGACY_PATH, + MAX_SIBLING_FILE_BYTES, + GODPLANS_VALIDATOR_HASHES, DOMAIN_CODES, detect, parsePlan, + validatePlanText, + loadPlan, + planExecutionState, parseAudit, summarize, loadAudit, @@ -853,6 +1328,8 @@ module.exports = { _private: { readFrontmatter, parseTasks, + parseRequirementDefinitions, + parsePlanFrontmatterState, parseFindings, parseCheckLedger, parseCompliance, @@ -863,6 +1340,8 @@ module.exports = { countFindings, countCheckOutcomes, remediationPriority, + inspectSiblingFile, + readRegularBufferOrNull, readRegularFileOrNull, hashPaths, PLAN_TASK_RE, diff --git a/lib/source-sync.js b/lib/source-sync.js index 455fb3b..0c34c9c 100644 --- a/lib/source-sync.js +++ b/lib/source-sync.js @@ -30,9 +30,10 @@ const SYSTEM_TARGETS = { pointerCandidates: [] }, // Sibling superskill directories are mdx-family, so their companions are - // .mdx (unlike the legacy .md companions in foreign dirs). PLAN.mdx and - // AUDIT.json and its generated AUDIT.mdx view are owned by godaudits and - // never receive fences, so pointerCandidates stays empty for both. + // .mdx (unlike the legacy .md companions in foreign dirs). PLAN.mdx, + // validate-plan.sh, AUDIT.json, and its generated AUDIT.mdx view remain + // owned by their sibling products and never receive fences, so + // pointerCandidates stays empty for both. godplans: { companionCandidates: ['.godplans/GODPOWERS-SYNC.mdx'], pointerCandidates: [] diff --git a/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md b/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md index c9a7b74..6d027aa 100644 --- a/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md +++ b/references/orchestration/GOD-ORCHESTRATOR-RUNBOOK.md @@ -159,7 +159,8 @@ Detection signals: `.claude/skills/`, `.codex/skills/` - BMAD: `.bmad-core/`, `bmad-core/`, `.bmad/`, `BMAD.md`, `docs/prd.md`, `docs/architecture.md`, `docs/roadmap.md` -- godplans: `.godplans/` -> godplans master plan (`PLAN.mdx`) +- godplans: `.godplans/` -> Godplans 1.1 master plan (`PLAN.mdx`) plus its + self-contained validator (`validate-plan.sh`) - godaudits: `.godaudits/` -> canonical godaudits state (`AUDIT.json`) plus a generated or legacy `AUDIT.mdx` report @@ -179,12 +180,16 @@ When signals are found: Downstream planning agents may read this artifact. They must cite it as supporting evidence only. -godplans/godaudits carve-out: these sibling artifacts are structured and -machine-verified (parse via `lib/sibling-artifacts.js`), so GP/GA task status +godplans/godaudits carve-out: canonical audit state and a complete Godplans +1.1 two-artifact contract are structured and machine-verified (inspect via +`lib/sibling-artifacts.js`), so GP/GA task status and R--n / A--n ids may be cited as [DECISION]-grade source facts ("the plan says X", cite the GP/R id). Product intent inferred beyond what -the plan or audit states stays [HYPOTHESIS]. Both directories are read-only -for Godpowers except during explicit GP/GA execution. A godaudits 2.x GA +the plan or audit states stays [HYPOTHESIS]. Legacy, incomplete, or unsupported +Godplans contracts stay [HYPOTHESIS]-grade and never authorize GP work. Both +directories are read-only for Godpowers except during explicit GP/GA +execution. GP work requires `approved` or `executing` status and a passing +`bash .godplans/validate-plan.sh .godplans/PLAN.mdx` gate. A godaudits 2.x GA completion updates reciprocal state in AUDIT.json, validates with `--write`, and regenerates derived views; generated MDX is never hand-edited. All other write-back happens only through the managed diff --git a/references/orchestration/MODE-DETECTION.md b/references/orchestration/MODE-DETECTION.md index 02e47fe..78ecb27 100644 --- a/references/orchestration/MODE-DETECTION.md +++ b/references/orchestration/MODE-DETECTION.md @@ -13,12 +13,14 @@ **Behavior**: run all tiers from PRD onwards. **Greenfield-with-plan**: an empty repo plus `.godplans/PLAN.mdx` is still -Mode A, but not a bare one. The arc imports the plan as Tier 1 seeds +Mode A, but not a bare one. The arc first verifies the Godplans 1.1 companion +at `.godplans/validate-plan.sh`, then imports the plan as Tier 1 seeds (`lib/planning-systems.importPlanningContext`; seeds marked `imported` with GP/R ids preserved) and routes to execution, so god-pm and god-architect -harden the imported seeds instead of interviewing from zero. PLAN.mdx stays -read-only; Godpowers edits it only when executing GP tasks under the plan's -own embedded executor rules. +harden the imported seeds instead of interviewing from zero. A legacy or +incomplete contract is hypothesis-grade context only. GP execution requires +`approved` or `executing` status plus a passing pinned validator command. +PLAN.mdx stays read-only outside that lifecycle-governed execution. ## Mode B: Gap-fill @@ -28,7 +30,7 @@ own embedded executor rules. - User describes an existing project they want to add Godpowers to - legacy planning, BMAD, or Superpowers planning context is detected and should be imported into Godpowers preparation artifacts -- `.godplans/PLAN.mdx` or `.godaudits/AUDIT.json` -> sibling superskill +- `.godplans/PLAN.mdx` plus validator, or `.godaudits/AUDIT.json` -> sibling superskill artifacts; import before planning (`/god-migrate`), never reconstruct over an existing master plan @@ -46,7 +48,7 @@ Codebase signals (for inferring partial completion): - `Dockerfile` + deploy config -> Deploy tier may be done - `.planning/`, `.legacy-planning/`, `_bmad-output/`, `.bmad/`, or Superpowers specs -> source-system import and managed sync-back may be needed -- `.godplans/PLAN.mdx` or `.godaudits/AUDIT.json` -> sibling plan/audit import +- `.godplans/PLAN.mdx` plus validator, or `.godaudits/AUDIT.json` -> sibling plan/audit import needed; sync-back goes through `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx` only diff --git a/routing/god-context-scan.yaml b/routing/god-context-scan.yaml index 7be832b..283208d 100644 --- a/routing/god-context-scan.yaml +++ b/routing/god-context-scan.yaml @@ -15,6 +15,7 @@ execution: reads: - .godpowers/state.json - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/routing/god-doctor.yaml b/routing/god-doctor.yaml index 66a18e1..3a84bd5 100644 --- a/routing/god-doctor.yaml +++ b/routing/god-doctor.yaml @@ -16,6 +16,7 @@ execution: reads: - .godpowers/state.json - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/routing/god-init.yaml b/routing/god-init.yaml index 880038d..6930689 100644 --- a/routing/god-init.yaml +++ b/routing/god-init.yaml @@ -18,6 +18,7 @@ execution: reads: - user-intent - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/routing/god-mode.yaml b/routing/god-mode.yaml index abf6d4f..cc5a685 100644 --- a/routing/god-mode.yaml +++ b/routing/god-mode.yaml @@ -23,6 +23,7 @@ execution: - .godpowers/prep/IMPORTED-CONTEXT.mdx - .godpowers/preflight/PREFLIGHT.mdx - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/routing/god-preflight.yaml b/routing/god-preflight.yaml index 46247d4..44757eb 100644 --- a/routing/god-preflight.yaml +++ b/routing/god-preflight.yaml @@ -16,6 +16,7 @@ execution: context: fresh reads: - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/routing/god-reconcile.yaml b/routing/god-reconcile.yaml index d75d5ba..fa9337f 100644 --- a/routing/god-reconcile.yaml +++ b/routing/god-reconcile.yaml @@ -28,6 +28,7 @@ execution: - .godpowers/todos/TODOS.mdx - .godpowers/threads/ - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/schema/state.v1.json b/schema/state.v1.json index 80333c3..f08ee09 100644 --- a/schema/state.v1.json +++ b/schema/state.v1.json @@ -101,7 +101,7 @@ }, "source-systems": { "type": "array", - "description": "Detected external planning systems imported into Godpowers and eligible for managed sync-back. Includes the sibling superskill artifacts godplans (.godplans/PLAN.mdx) and godaudits (.godaudits/AUDIT.json, with legacy AUDIT.mdx fallback).", + "description": "Detected external planning systems imported into Godpowers and eligible for managed sync-back. Includes the Godplans 1.1 two-artifact contract (.godplans/PLAN.mdx plus validate-plan.sh), legacy PLAN context, and Godaudits canonical state (.godaudits/AUDIT.json, with legacy AUDIT.mdx fallback).", "items": { "type": "object", "required": ["id", "name", "confidence", "files", "import-hash"], diff --git a/scripts/test-sibling-artifacts.js b/scripts/test-sibling-artifacts.js index 69e64b2..a9ed7b3 100644 --- a/scripts/test-sibling-artifacts.js +++ b/scripts/test-sibling-artifacts.js @@ -9,6 +9,7 @@ const fs = require('fs'); const path = require('path'); +const crypto = require('crypto'); const siblingArtifacts = require('../lib/sibling-artifacts'); const planningSystems = require('../lib/planning-systems'); @@ -83,6 +84,70 @@ const PLAN_FIXTURE = [ '- 2026-07-01 planned phase 1.' ].join('\n'); +function godplans11Plan(status = 'approved', taskCount = 1) { + const taskLines = []; + for (let number = 1; number <= taskCount; number++) { + const id = `GP-${100 + number}`; + taskLines.push( + `- [ ] ${id} [W1.1] Verify release condition ${number}`, + ' - Files: package.json', + ' - Depends on: none', + ' - Reuses: existing release check from package.json', + ' - Acceptance: release check exits zero; package metadata remains valid', + ' - Verify: `npm run release:check`', + ' - Requirements: R-1.1, R-CODE-1, R-SEC-1', + '' + ); + } + return [ + '---', + 'name: demo', + 'plan_version: 2', + `status: ${status}`, + 'created: 2026-07-01', + 'updated: 2026-07-13', + 'mode: replan', + 'archetype: cli-tool', + 'domains_applicable: [product, security, code-quality]', + 'domains_excluded: []', + 'progress:', + ' phases_total: 1', + ' phases_done: 0', + ` tasks_total: ${taskCount}`, + ' tasks_done: 0', + '---', + '', + '# Demo master plan', + '', + 'Done means the release verification command passes.', + '', + '## Requirements', + '', + 'R-1.1: WHEN the maintainer verifies the release THE SYSTEM SHALL exit zero.', + '', + '## Phases', + '', + '## Phase 1: Verification', + '', + '### Wave 1.1', + '', + ...taskLines, + 'Checkpoint: the release gate passes.', + '', + '## Open Questions', + '', + '## Rules for executing agents', + '', + 'Run the validator before execution.', + '', + '## Session log', + '', + '- 2026-07-13 replanned with godplans v1.1.0.' + ].join('\n'); +} + +const TEST_PLAN_VALIDATOR = '#!/usr/bin/env bash\nexit 0\n'; + const AUDIT_FIXTURE = [ '---', 'name: demo', @@ -424,6 +489,135 @@ test('parsePlan tolerates empty text and missing sections', () => { assert(Object.keys(bare.frontmatter).length === 0, 'missing frontmatter should be {}'); }); +test('validatePlanText mirrors the Godplans 1.1 structural and lifecycle gate', () => { + const valid = siblingArtifacts.validatePlanText(godplans11Plan('approved')); + assert(valid.valid, `valid plan errors: ${valid.errors.join('; ')}`); + assert(valid.derived.tasks_total === 1 && valid.derived.phases_total === 1, + `derived counters: ${JSON.stringify(valid.derived)}`); + + const executionBlocked = siblingArtifacts.validatePlanText( + godplans11Plan('planning'), + { allowPlanning: false } + ); + assert(executionBlocked.errors.some((error) => error.includes('execution requires status')), + `execution lifecycle error missing: ${executionBlocked.errors.join('; ')}`); + + const missingReuse = godplans11Plan().replace( + ' - Reuses: existing release check from package.json\n', + '' + ); + const invalid = siblingArtifacts.validatePlanText(missingReuse); + assert(!invalid.valid && invalid.errors.includes('GP-101 missing required field: Reuses'), + `Reuses validation missing: ${invalid.errors.join('; ')}`); +}); + +test('loadPlan verifies the two-artifact contract without executing shell', () => { + const tmp = mkProject('godpowers-plan-contract-'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan('approved')); + + let loaded = siblingArtifacts.loadPlan(tmp); + assert(!loaded.contract.complete && loaded.contract.reason === 'validator-missing', + `missing validator reason: ${loaded.contract.reason}`); + + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + loaded = siblingArtifacts.loadPlan(tmp); + assert(!loaded.contract.complete && loaded.contract.reason === 'validator-unsupported', + `unknown validator reason: ${loaded.contract.reason}`); + + const hash = `sha256:${crypto.createHash('sha256').update(TEST_PLAN_VALIDATOR).digest('hex')}`; + loaded = siblingArtifacts.loadPlan(tmp, { + trustedValidatorHashes: { [hash]: 'test-1.1.0' } + }); + assert(loaded.contract.complete && loaded.contract.executionEligible, + `trusted contract: ${JSON.stringify(loaded.contract)}`); + assert(loaded.contract.reason === 'ready-for-validator', `reason: ${loaded.contract.reason}`); + assert(loaded.contract.validatorCommand === 'bash .godplans/validate-plan.sh .godplans/PLAN.mdx', + `command: ${loaded.contract.validatorCommand}`); +}); + +test('loadPlan rejects non-executable and symlinked validator companions', () => { + const tmp = mkProject('godpowers-plan-validator-safety-'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan()); + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o644); + let loaded = siblingArtifacts.loadPlan(tmp); + assert(loaded.contract.reason === 'validator-not-executable', + `non-executable reason: ${loaded.contract.reason}`); + + fs.rmSync(path.join(tmp, '.godplans', 'validate-plan.sh')); + const outside = mkProject('godpowers-plan-validator-outside-'); + writeRel(outside, 'validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.symlinkSync( + path.join(outside, 'validate-plan.sh'), + path.join(tmp, '.godplans', 'validate-plan.sh') + ); + loaded = siblingArtifacts.loadPlan(tmp); + assert(loaded.contract.reason === 'validator-unreadable-or-non-regular', + `symlink reason: ${loaded.contract.reason}`); +}); + +test('planExecutionState blocks planning and done plans before GP dispatch', () => { + const hash = `sha256:${crypto.createHash('sha256').update(TEST_PLAN_VALIDATOR).digest('hex')}`; + const opts = { trustedValidatorHashes: { [hash]: 'test-1.1.0' } }; + for (const [status, reason] of [ + ['planning', 'awaiting-explicit-approval'], + ['done', 'plan-closed'] + ]) { + const tmp = mkProject(`godpowers-plan-${status}-`); + let planText = godplans11Plan(status); + if (status === 'done') { + planText = planText + .replace(' phases_done: 0', ' phases_done: 1') + .replace(' tasks_done: 0', ' tasks_done: 1') + .replace('- [ ] GP-101', '- [x] GP-101'); + } + writeRel(tmp, '.godplans/PLAN.mdx', planText); + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + const execution = siblingArtifacts.planExecutionState(tmp, opts); + assert(!execution.executionEligible && execution.reason === reason, + `${status} gate: ${JSON.stringify(execution)}`); + assert(execution.nextTasks.length === 0, `${status} dispatched GP work`); + } +}); + +test('loadPlan rejects a done lifecycle that still has open tasks', () => { + const tmp = mkProject('godpowers-plan-inconsistent-done-'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan('done')); + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + const hash = `sha256:${crypto.createHash('sha256').update(TEST_PLAN_VALIDATOR).digest('hex')}`; + const loaded = siblingArtifacts.loadPlan(tmp, { + trustedValidatorHashes: { [hash]: 'test-1.1.0' } + }); + assert(!loaded.contract.complete && loaded.contract.reason === 'lifecycle-inconsistent', + `inconsistent done gate: ${JSON.stringify(loaded.contract)}`); + assert(loaded.plan.lifecycle.errors.some((error) => error.includes('open task')), + `lifecycle errors: ${loaded.plan.lifecycle.errors.join('; ')}`); +}); + +test('planExecutionState dispatches the first eligible task from an approved plan', () => { + const tmp = mkProject('godpowers-plan-approved-dispatch-'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan('approved', 2)); + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + const hash = `sha256:${crypto.createHash('sha256').update(TEST_PLAN_VALIDATOR).digest('hex')}`; + const execution = siblingArtifacts.planExecutionState(tmp, { + trustedValidatorHashes: { [hash]: 'test-1.1.0' } + }); + assert(execution.executionEligible && execution.reason === 'ready-for-validator', + `approved execution gate: ${JSON.stringify(execution)}`); + assert(execution.nextTasks.length === 1 && execution.nextTasks[0].id === 'GP-101', + `next tasks: ${execution.nextTasks.map((task) => task.id).join(', ')}`); +}); + +test('the trusted Godplans validator catalog pins version 1.1.0', () => { + const expected = 'sha256:cec8691bb32f272bfe29acdab435be6f95d55405a914fc6ff33277aca5c8eb6b'; + assert(siblingArtifacts.GODPLANS_VALIDATOR_HASHES[expected] === '1.1.0', + 'official Godplans 1.1.0 validator hash missing'); +}); + test('parseAudit recounts GA tasks and parses the finding grammar', () => { const audit = siblingArtifacts.parseAudit(AUDIT_FIXTURE); assert(audit.counts.total === 2 && audit.counts.done === 1 && audit.counts.open === 1, @@ -727,6 +921,29 @@ test('staleness matches the recorded import hash and flags edits', () => { assert(stale[0].recordedHash !== stale[0].currentHash, 'hashes should differ'); }); +test('godplans staleness hashes PLAN and validator but ignores sync-back', () => { + const tmp = mkProject('godpowers-plan-staleness-'); + state.init(tmp, 'godplans-contract-staleness-test'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan()); + writeRel(tmp, '.godplans/validate-plan.sh', TEST_PLAN_VALIDATOR); + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + planningSystems.importPlanningContext(tmp); + + writeRel(tmp, '.godplans/GODPOWERS-SYNC.mdx', '# Generated sync back\n'); + let drift = siblingArtifacts.staleness(tmp, state.read(tmp)); + assert(drift.length === 1 && drift[0].stale === false, + 'sync-back companion caused false Godplans drift'); + + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o644); + drift = siblingArtifacts.staleness(tmp, state.read(tmp)); + assert(drift[0].stale === true, 'validator executable-mode drift was missed'); + + fs.chmodSync(path.join(tmp, '.godplans', 'validate-plan.sh'), 0o755); + fs.appendFileSync(path.join(tmp, '.godplans', 'validate-plan.sh'), '# changed\n'); + drift = siblingArtifacts.staleness(tmp, state.read(tmp)); + assert(drift[0].stale === true, 'changed validator did not stale the import'); +}); + test('state.detectDrift emits WARN sibling-stale drift after a plan edit', () => { const tmp = mkProject('godpowers-sibling-'); state.init(tmp, 'sibling-drift-test'); @@ -763,6 +980,36 @@ test('godaudits staleness follows canonical JSON and ignores generated MDX chang console.log('\n Planning-system integration for sibling artifacts\n'); +test('planning-systems reads Godplans beyond the foreign-file sampling cap', () => { + const tmp = mkProject('godpowers-large-plan-'); + const filler = `${'Plan-specific context stays readable.\n'.repeat(4000)}\n`; + const plan = godplans11Plan().replace('## Requirements', `${filler}## Requirements`); + assert(Buffer.byteLength(plan) > 80 * 1024, 'large PLAN fixture is not large'); + writeRel(tmp, '.godplans/PLAN.mdx', plan); + const detection = planningSystems.detect(tmp); + const godplans = detection.systems.find((system) => system.id === 'godplans'); + const record = godplans && godplans.files.find((file) => file.path === '.godplans/PLAN.mdx'); + assert(record && record.bytes === Buffer.byteLength(plan), + `PLAN bytes were truncated: ${record && record.bytes}`); + assert(record.plan && record.plan.validation.valid, + `large PLAN validation: ${record && record.plan.validation.errors.join('; ')}`); +}); + +test('plan-derived seeds preserve GP tasks beyond the old six-signal cap', () => { + const tmp = mkProject('godpowers-plan-seed-depth-'); + state.init(tmp, 'godplans-seed-depth-test'); + writeRel(tmp, '.godplans/PLAN.mdx', godplans11Plan('approved', 10)); + planningSystems.importPlanningContext(tmp); + const roadmap = fs.readFileSync(path.join(tmp, '.godpowers', 'roadmap', 'ROADMAP.mdx'), 'utf8'); + const build = fs.readFileSync(path.join(tmp, '.godpowers', 'prep', 'IMPORTED-BUILD-STATE.mdx'), 'utf8'); + const prd = fs.readFileSync(path.join(tmp, '.godpowers', 'prd', 'PRD.mdx'), 'utf8'); + assert(roadmap.includes('GP-110'), 'roadmap seed dropped the tenth GP task'); + assert(build.includes('GP-110'), 'build seed dropped the tenth GP task'); + assert(prd.includes('GP-110'), 'PRD seed dropped GP traceability'); + assert(roadmap.includes('Verify: npm run release:check'), 'Verify command was not preserved'); + assert(roadmap.includes('Reuses: existing release check'), 'Reuses field was not preserved'); +}); + test('planning-systems detects godplans with forced high confidence', () => { const tmp = mkProject('godpowers-sibling-'); writeRel(tmp, '.godplans/PLAN.mdx', PLAN_FIXTURE); @@ -915,7 +1162,8 @@ test('importPlanningContext preserves GP/R ids in plan-derived seeds', () => { assert(result.writtenArtifacts.includes('prd/PRD.mdx'), 'PRD seed not written'); const seed = fs.readFileSync(path.join(tmp, '.godpowers', 'prd', 'PRD.mdx'), 'utf8'); assert(seed.includes('GP-102'), 'GP id not preserved verbatim in seed'); - assert(seed.includes('[DECISION] Imported signal'), 'plan signals should be decision-grade'); + assert(seed.includes('[HYPOTHESIS] Imported signal'), + 'legacy plan without validator should remain hypothesis-grade'); }); test('IMPORTED-CONTEXT carries recounted sibling sections and enumeration', () => { diff --git a/skills/god-capture.md b/skills/god-capture.md index bc364c7..188a744 100644 --- a/skills/god-capture.md +++ b/skills/god-capture.md @@ -44,7 +44,8 @@ Default to `/god-note` when no priority, backlog, or trigger signal exists. - Do not write notes, todos, backlog items, or seeds directly from this dispatcher. - Do not assign priority unless the user provided priority evidence. - Keep each capture leaf callable as a direct shortcut. -- Never edit `.godplans/PLAN.mdx`, `.godaudits/AUDIT.json`, or generated +- Never edit `.godplans/PLAN.mdx`, `.godplans/validate-plan.sh`, + `.godaudits/AUDIT.json`, or generated `.godaudits/AUDIT.mdx` from capture flows; they are read-only here and sync-back happens only through the managed GODPOWERS-SYNC.mdx companions. diff --git a/skills/god-context-scan.md b/skills/god-context-scan.md index 976d091..78ea1fa 100644 --- a/skills/god-context-scan.md +++ b/skills/god-context-scan.md @@ -50,7 +50,8 @@ called drifts. `.godpowers/CHECKPOINT.mdx` frontmatter + facts. 3. `lib/state.detectDrift(projectRoot)` cross-checks artifacts on disk match their recorded hashes. This includes source-systems - import hashes for sibling artifacts: when `.godplans/PLAN.mdx` or canonical + import hashes for sibling artifacts: when `.godplans/PLAN.mdx` plus its + recorded validator companion, or canonical `.godaudits/AUDIT.json` no longer matches the hash recorded at import (the user re-ran godplans/godaudits mid-session), report a WARN drift "sibling artifact changed since import" with suggested diff --git a/skills/god-doctor.md b/skills/god-doctor.md index f1ed8b7..7882024 100644 --- a/skills/god-doctor.md +++ b/skills/god-doctor.md @@ -35,11 +35,16 @@ Run a system-state diagnostic. Build nothing. Touch nothing. Report only. 8. Does `state.json` know the current Godpowers feature set? 9. Are managed AI-tool context fences present when tools are detected? 10. When `state.json` `source-systems` contains a godplans or godaudits - entry, re-hash `.godplans/PLAN.mdx` or canonical `.godaudits/AUDIT.json` + entry, re-hash the canonical `.godplans/PLAN.mdx` plus its recorded + `.godplans/validate-plan.sh` companion, or canonical `.godaudits/AUDIT.json` and compare with the recorded import hash (`lib/sibling-artifacts.js` staleness). A mismatch means the plan was replanned or the audit re-run since import: report [WARN] "imported plan/audit stale; run /god-migrate to re-import". +11. Call `lib/sibling-artifacts.loadPlan(projectRoot)` when PLAN.mdx exists. + Report [ERROR] for a missing, non-regular, non-executable, or unsupported + validator, structural errors, or inconsistent `done` state. Report [INFO] + for `planning` awaiting explicit approval and for a closed `done` plan. ### External integration health 1. Is impeccable present? `node_modules/impeccable` or `~/.claude/skills/impeccable`? @@ -113,7 +118,8 @@ as a read-only diagnostic. It reports: - missing managed AI-tool context fences - unimported legacy planning, BMAD, or Superpowers evidence that should route to `/god-migrate` -- unimported sibling `.godplans/PLAN.mdx` or `.godaudits/AUDIT.json` artifacts +- unimported sibling `.godplans/PLAN.mdx` plus validator, or + `.godaudits/AUDIT.json` artifacts that should route to `/god-migrate` (these detect at high confidence; no greenfieldifier judgment needed) - `god-greenfieldifier` recommendation when migration evidence is low diff --git a/skills/god-init.md b/skills/god-init.md index 4ae5294..4196259 100644 --- a/skills/god-init.md +++ b/skills/god-init.md @@ -44,7 +44,8 @@ needs to specify a mode. `.claude/skills/`, `.codex/skills/` - BMAD: `.bmad-core/`, `bmad-core/`, `.bmad/`, `BMAD.md`, `docs/prd.md`, `docs/architecture.md`, `docs/roadmap.md` - - godplans: `.godplans/PLAN.mdx` (godplans master plan) + - godplans: `.godplans/PLAN.mdx` plus `.godplans/validate-plan.sh` + (Godplans 1.1 master plan contract; PLAN-only inputs are legacy context) - godaudits: `.godaudits/AUDIT.json` (canonical 2.x audit state), with `.godaudits/AUDIT.mdx` as a generated or legacy fallback - If any are detected, summarize useful signals into @@ -55,8 +56,9 @@ needs to specify a mode. detected, and record the systems in `state.json` `source-systems`. Report this as `Agent: none, local runtime only`. - godplans and godaudits are sibling superskills, not legacy systems. - Announce them plainly: "Detected: a godplans master plan. I will import - it instead of re-planning." or "Detected: a godaudits audit report. I + Announce them plainly: "Detected: a Godplans master plan. I will verify + its plan and validator contract, then import it instead of re-planning." + or "Detected: a godaudits audit report. I will import its findings instead of rediscovering them." Their artifacts are read-only for Godpowers except when executing GP/GA tasks; use `lib/sibling-artifacts.js` (detect, summarize) for parsing. @@ -129,7 +131,7 @@ needs to specify a mode. - Run Scale Detection (trivial/small/medium/large/enterprise) - Write `.godpowers/prep/INITIAL-FINDINGS.mdx` - Run planning-system context detection for legacy planning, Superpowers, - BMAD, godplans (`.godplans/PLAN.mdx`), and godaudits + BMAD, godplans (`.godplans/PLAN.mdx` plus validator), and godaudits (`.godaudits/AUDIT.json`, with legacy `.godaudits/AUDIT.mdx` fallback) - Write `.godpowers/prep/IMPORTED-CONTEXT.mdx` when useful context exists - Run automatic planning-system import through @@ -215,7 +217,8 @@ If legacy planning, Superpowers, BMAD, godplans, godaudits, or similar planning context is detected, create `.godpowers/prep/IMPORTED-CONTEXT.mdx`. This artifact is preparation context, not source of truth. It feeds PRD, architecture, roadmap, and stack decisions as hypothesis-level input only, -with one carve-out: facts read from `.godplans/PLAN.mdx` or canonical +with one carve-out: facts read from a complete Godplans 1.1 PLAN plus pinned +validator contract, or canonical `.godaudits/AUDIT.json` (GP/GA task status, R--n and A--n ids, compiled scores and coverage) may be cited as [DECISION]-grade authored state; product claims inferred beyond the plan stay [HYPOTHESIS]. diff --git a/skills/god-migrate.md b/skills/god-migrate.md index a64b0c1..1407ecc 100644 --- a/skills/god-migrate.md +++ b/skills/god-migrate.md @@ -23,7 +23,8 @@ Detect and migrate adjacent planning systems into Godpowers. - A project already has BMAD `_bmad/`, `_bmad-output/`, `.bmad-core/`, or `.bmad/` context. - A project already has Superpowers specs, plans, or project-local skills. -- A project already has a godplans master plan at `.godplans/PLAN.mdx`. +- A project already has a Godplans master plan at `.godplans/PLAN.mdx`, with + `.godplans/validate-plan.sh` for a complete Godplans 1.1 emission. - A project already has canonical godaudits state at `.godaudits/AUDIT.json` or a legacy 1.x report at `.godaudits/AUDIT.mdx`. - The user wants a reversible migration path into Godpowers. @@ -80,10 +81,14 @@ This: For godplans and godaudits sources, parsing runs through `lib/sibling-artifacts.js` (detect, summarize, remediationTasks, staleness). -PLAN.mdx is authored, structured intent: seeds derived from it preserve GP -task ids and R--n requirement ids verbatim and may carry -[DECISION]-grade citations of the plan (cite the GP/R id); anything inferred -beyond the plan stays [HYPOTHESIS]. AUDIT.json is validated prior audit state: +PLAN.mdx is authored, structured intent: seeds derived from it preserve every +GP task id, task lifecycle field, Verify command, and R--n requirement id +needed by the destination seed. `loadPlan` performs a non-executing structural +preflight, verifies the validator is the pinned executable Godplans 1.1.0 +companion, and exposes lifecycle status. Complete emissions may carry +[DECISION]-grade citations by GP/R id. Missing, invalid, legacy, or unsupported +companions remain [HYPOTHESIS]-grade context and never authorize GP execution. +AUDIT.json is validated prior audit state: its explicit check outcomes, secret-safe evidence metadata, compliance result, accepted risks, open questions, score caps, coverage, F--n findings, and typed open GA tasks seed the harden tier and traceable todo entries. Legacy MDX @@ -115,7 +120,7 @@ This: | legacy planning variant | `.legacy-planning/` files | prep context and seeds when source files are readable | `.legacy-planning/GODPOWERS-SYNC.md` | | BMAD | `_bmad-output/planning-artifacts/PRD.md`, `architecture.md`, epics, stories, sprint status | prep context, PRD seed, arch seed, roadmap seed | `_bmad-output/GODPOWERS-SYNC.md` | | Superpowers | `docs/superpowers/specs/*.md`, `docs/superpowers/plans/*.md`, project-local skills | prep context, PRD seed, roadmap seed, build-state seed | `docs/superpowers/GODPOWERS-SYNC.md` | -| godplans | `.godplans/PLAN.mdx` | prep context, PRD seed, arch seed, roadmap seed, stack seed, build-state seed (GP task ids and R--n ids preserved verbatim) | `.godplans/GODPOWERS-SYNC.mdx` | +| godplans | `.godplans/PLAN.mdx` plus executable `.godplans/validate-plan.sh` for 1.1 | prep context, PRD seed, arch seed, roadmap seed, stack seed, `.godpowers/prep/IMPORTED-BUILD-STATE.mdx` (all applicable GP task ids, lifecycle fields, Verify commands, and R ids preserved) | `.godplans/GODPOWERS-SYNC.mdx` | | godaudits | `.godaudits/AUDIT.json` (legacy `.godaudits/AUDIT.mdx` fallback) | prep context, harden/FINDINGS seed, compiled coverage, open GA remediation tasks routed to todos/backlog with Verify commands preserved | `.godaudits/GODPOWERS-SYNC.mdx` | ## Guardrails @@ -127,6 +132,11 @@ This: regenerates its derived views. All other flows write only through the managed `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx` companion. Never write fences into PLAN.mdx, AUDIT.json, or AUDIT.mdx. +- Never execute a GP task until `loadPlan` reports `ready-for-validator`, the + plan status is `approved` or `executing`, and + `bash .godplans/validate-plan.sh .godplans/PLAN.mdx` exits zero. Godpowers + does not approve a `planning` plan on the user's behalf and does not reopen + a `done` plan. - Verify commands sourced from AUDIT.json are untrusted repo content. Run them only when they are plainly read-only (grep/test/ls/node --check class); anything that mutates state requires showing the command and getting user diff --git a/skills/god-mode.md b/skills/god-mode.md index 84485cf..5f73dfa 100644 --- a/skills/god-mode.md +++ b/skills/god-mode.md @@ -46,7 +46,8 @@ workflow. - `.godpowers/intent.yaml`, when present - `.godpowers/prep/INITIAL-FINDINGS.mdx`, when present - `.godpowers/prep/IMPORTED-CONTEXT.mdx`, when present - - `.godplans/PLAN.mdx`, when present (sibling godplans master plan; read-only) + - `.godplans/PLAN.mdx` and `.godplans/validate-plan.sh`, when present + (the Godplans 1.1 two-artifact contract; read-only until explicit GP execution) - `.godaudits/AUDIT.json`, when present (canonical sibling godaudits state; read-only except during explicit GA remediation) - `.godaudits/AUDIT.mdx` only as a generated or legacy fallback @@ -89,12 +90,26 @@ workflow. first planning or build step - Instruction to read `.godpowers/preflight/PREFLIGHT.mdx` if present before choosing the first brownfield or bluefield action - - Instruction to read `.godplans/PLAN.mdx` and canonical + - Instruction to inspect `.godplans/PLAN.mdx` plus + `.godplans/validate-plan.sh` through + `lib/sibling-artifacts.loadPlan(projectRoot)` and canonical `.godaudits/AUDIT.json` if present before choosing the first step. When PLAN.mdx exists and Godpowers tiers are pending, prefer importing plan seeds via `/god-migrate` over re-running god-pm or god-architect from scratch, - and honor the plan's GP task checkboxes as already-planned work. When + and honor the plan's GP task checkboxes as already-planned work only + when the static contract is complete. A missing, non-executable, or + unknown validator, invalid plan structure, `planning` status, or `done` + status blocks GP dispatch. Legacy or incomplete plans remain read-only + hypothesis-grade migration context. For an `approved` or `executing` + plan, run `bash .godplans/validate-plan.sh .godplans/PLAN.mdx` + immediately before GP work and stop if it exits non-zero. The first + executor changes `approved` to `executing`; each passing task updates + its checkbox, derived counters, `updated` date, and session log in the + same edit. Only a completed final Verification phase may change the + plan to `done`. A material scope change returns it to `planning`, bumps + `plan_version`, preserves completed tasks, and requires fresh approval. + When AUDIT.json exists, feed its open typed GA remediation tasks into the repair loop instead of rediscovering them. A legacy AUDIT.mdx is a fallback only. Sibling sources are read-only except during explicit diff --git a/skills/god-preflight.md b/skills/god-preflight.md index dd222db..3fa0cc1 100644 --- a/skills/god-preflight.md +++ b/skills/god-preflight.md @@ -43,9 +43,11 @@ It answers: - README, docs, ADRs, architecture notes, env examples, AGENTS.md - test presence, test command discoverability, coverage signals - deploy, observability, security, dependency, and ownership signals - - `.godplans/PLAN.mdx` and `.godaudits/AUDIT.json` presence (sibling - superskill artifacts; a master plan is direct arc-readiness evidence, - validated audit state provides score plus coverage; both are read-only) + - `.godplans/PLAN.mdx`, `.godplans/validate-plan.sh`, and + `.godaudits/AUDIT.json` presence (a Godplans 1.1 master plan is direct + arc-readiness evidence only when its pinned validator companion is + regular, executable, structurally valid, and lifecycle-consistent; + validated audit state provides score plus coverage; all are read-only) 4. Produce `.godpowers/preflight/PREFLIGHT.mdx`. 5. Do not edit source files, planning artifacts, configs, or docs outside `.godpowers/preflight/`. @@ -59,7 +61,7 @@ Use these dimensions: | Dimension | Looks for | |---|---| | Repo shape | Project type, framework, package manager, entry points, source layout | -| Arc readiness | Product direction, architecture notes, roadmap, deploy story, decision records, godplans master plan (`.godplans/PLAN.mdx`) | +| Arc readiness | Product direction, architecture notes, roadmap, deploy story, decision records, complete Godplans contract (`.godplans/PLAN.mdx` plus validator) | | Pillar readiness | Tests, security, docs, observability, maintainability, UX, deployability | | Godpowers readiness | Disk artifacts, AGENTS.md, commands, progress state, safe agent boundaries | | Refactor risk | Coupling, missing tests, unclear ownership, ambiguous runtime paths | @@ -134,7 +136,7 @@ Use the report to choose the next pass: |---|---| | Missing basic project state | `/god-init` | | Unknown legacy structure | `/god-archaeology` | -| godplans `PLAN.mdx` exists | `/god-migrate` (import the plan; do NOT reconstruct) | +| Godplans `PLAN.mdx` exists | inspect validator, then `/god-migrate`; incomplete contracts stay context-only | | godaudits `AUDIT.json` exists | `/god-migrate` then `/god-audit` in prior-audit mode (consume validated state, do not re-derive) | | Existing code lacks planning artifacts | `/god-reconstruct` | | Debt dominates delivery risk | `/god-tech-debt` | diff --git a/skills/god-reconcile.md b/skills/god-reconcile.md index 9a2bf60..915f843 100644 --- a/skills/god-reconcile.md +++ b/skills/god-reconcile.md @@ -97,7 +97,7 @@ Runtime and repository surfaces: SURFACE: [fresh | needs-surface-sync | needs-safe-fix | needs-human-review] FEATURES: [fresh | needs-awareness-refresh | needs-migration-judgment] SOURCE: [not-applicable | fresh | needs-sync-back | blocked-by-conflict] - PLAN: [not-applicable | planned-in-godplans GP- | not-in-plan | plan-conflict] + PLAN: [not-applicable | contract-incomplete | awaiting-approval | closed | planned-in-godplans GP- | not-in-plan | plan-conflict] AUDIT: [not-applicable | addresses-ga-task GA- | invalidates-finding F- | unrelated] HOST: [full | degraded | unknown] diff --git a/skills/god-reconstruct.md b/skills/god-reconstruct.md index c25323b..2031cc4 100644 --- a/skills/god-reconstruct.md +++ b/skills/god-reconstruct.md @@ -21,11 +21,13 @@ Derive planning artifacts from existing code. ## Setup -0. Check for `.godplans/PLAN.mdx`. If present, route to `/god-migrate` import - first: PLAN.mdx is authored planning, strictly better than - reverse-engineering. Reconstruction should then only fill gaps the plan - does not cover, using the plan as HIGH-confidence intent evidence (cite - GP/R ids) for those gaps. The plan file itself stays read-only. +0. Check for `.godplans/PLAN.mdx`. If present, inspect its + `.godplans/validate-plan.sh` companion and route to `/god-migrate` import + first. A complete Godplans 1.1 contract is authored planning, strictly + better than reverse-engineering. Reconstruction should then only fill gaps + the plan does not cover, using GP/R ids for those gaps. A legacy or + incomplete contract remains hypothesis-grade context and does not authorize + GP execution. Both files stay read-only here. 1. Verify codebase exists (Mode B) 2. Optional: run /god-archaeology first for richer input 3. Spawn god-reconstructor in fresh context diff --git a/templates/IMPORTED-CONTEXT.mdx b/templates/IMPORTED-CONTEXT.mdx index bbcaa68..325aa0f 100644 --- a/templates/IMPORTED-CONTEXT.mdx +++ b/templates/IMPORTED-CONTEXT.mdx @@ -36,6 +36,9 @@ ## Executable Plan Signals (godplans only) - [DECISION] Plan file: [.godplans/PLAN.mdx or "not detected"]. +- [DECISION] Validator companion: [.godplans/validate-plan.sh status: pinned executable / missing / non-regular / not executable / unsupported]. +- [DECISION] Godplans contract: [complete / incomplete with reason]. +- [DECISION] Lifecycle: [planning awaiting approval / approved / executing / done closed / invalid]. - [DECISION] GP task counts: [total / done, recounted from checkboxes, not frontmatter]. - [DECISION] Requirement id domains present: [`R--n` domains, e.g. `R-SEC`, `R-DB`, or "none"]. - [DECISION] Executor rules block present: [yes / no]. @@ -56,7 +59,7 @@ - [DECISION] This artifact must not override native Pillars files under `agents/*.md`. - [DECISION] If imported context conflicts with user intent or a Godpowers artifact, the Godpowers artifact wins and the conflict becomes an open question. - [DECISION] PRD, architecture, roadmap, and stack agents should cite imported signals as `[HYPOTHESIS]` until confirmed by Godpowers artifacts or the user. -- [DECISION] Carve-out: facts read from `.godplans/PLAN.mdx` or `.godaudits/AUDIT.json` (GP/GA task status, `R--n` and `A--n` ids, compiled scores and coverage) are `[DECISION]`-grade citations of authored state when cited by id; product claims inferred beyond the plan stay `[HYPOTHESIS]`. -- [DECISION] `.godplans/` and `.godaudits/` artifacts are read-only for Godpowers except during explicit GP/GA task execution; a godaudits 2.x completion updates and validates AUDIT.json, then regenerates derived views, while all other write-back uses only `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx`. +- [DECISION] Carve-out: facts from a complete Godplans 1.1 PLAN plus pinned validator contract, or `.godaudits/AUDIT.json` (GP/GA task status, `R--n` and `A--n` ids, compiled scores and coverage) are `[DECISION]`-grade citations when cited by id; legacy or incomplete PLAN facts and product claims inferred beyond the sources stay `[HYPOTHESIS]`. +- [DECISION] `.godplans/` and `.godaudits/` artifacts are read-only for Godpowers except during lifecycle-gated GP/GA task execution; validate-plan.sh is never edited, a godaudits 2.x completion updates and validates AUDIT.json then regenerates derived views, and all other write-back uses only `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx`. - [DECISION] Open GA tasks are mirrored into a replaceable managed section of `.godpowers/todos/TODOS.mdx`; user-owned todo content outside that section is preserved. - [DECISION] Sync-back must preserve source-system context through managed companion files or fences, not by overwriting arbitrary source-system artifacts. diff --git a/templates/INITIAL-FINDINGS.mdx b/templates/INITIAL-FINDINGS.mdx index 75c705c..922570c 100644 --- a/templates/INITIAL-FINDINGS.mdx +++ b/templates/INITIAL-FINDINGS.mdx @@ -27,7 +27,7 @@ - [HYPOTHESIS] Legacy planning context found: [paths or "none detected"]. - [HYPOTHESIS] Superpowers context found: [paths or "none detected"]. - [HYPOTHESIS] BMAD context found: [paths or "none detected"]. -- [HYPOTHESIS] godplans master plan found: [.godplans/PLAN.mdx or "none detected"]. +- [HYPOTHESIS] Godplans master plan contract found: [.godplans/PLAN.mdx plus validator status, legacy PLAN-only, or "none detected"]. - [HYPOTHESIS] godaudits audit found: [.godaudits/AUDIT.json, legacy AUDIT.mdx, or "none detected"]. - [HYPOTHESIS] Pillars context health: [present / partial / initialized]. - [HYPOTHESIS] Imported context artifact: [.godpowers/prep/IMPORTED-CONTEXT.mdx or "not created"]. @@ -59,6 +59,6 @@ - [DECISION] If this artifact conflicts with `.godpowers/intent.yaml`, `.godpowers/state.json`, `PROGRESS.md`, or a completed Godpowers artifact, the Godpowers artifact wins. - [DECISION] Pillars files under `agents/*.md` are the native project context layer for Godpowers commands. - [DECISION] Imported legacy planning, Superpowers, and BMAD signals must be converted into Godpowers-native artifacts, not preserved as parallel state. -- [DECISION] Sibling `.godplans/PLAN.mdx` and canonical `.godaudits/AUDIT.json` artifacts stay owned by their products: import seeds from them, keep source and generated views read-only outside task execution, and write back only through `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx`. +- [DECISION] Sibling Godplans PLAN plus validator and canonical `.godaudits/AUDIT.json` artifacts stay owned by their products: import seeds from them, keep source and generated views read-only outside lifecycle-gated task execution, and write back only through `.godplans/GODPOWERS-SYNC.mdx` or `.godaudits/GODPOWERS-SYNC.mdx`. - [DECISION] Managed sync-back files may be written only by Godpowers-owned fences or companion paths. - [DECISION] Host capability and dogfood findings inform routing, but completed Godpowers artifacts remain authoritative. diff --git a/workflows/brownfield-arc.yaml b/workflows/brownfield-arc.yaml index 97a8c55..75b4962 100644 --- a/workflows/brownfield-arc.yaml +++ b/workflows/brownfield-arc.yaml @@ -31,9 +31,9 @@ jobs: # Local runtime call: lib/planning-systems.importPlanningContext. Imports # sibling godplans/godaudits artifacts (and legacy planning systems) into - # prep context and seed artifacts before reconstruction runs. PLAN.mdx and - # AUDIT.json and generated AUDIT.mdx stay read-only during import; parsing - # runs through lib/sibling-artifacts.js. + # prep context and seed artifacts before reconstruction runs. PLAN.mdx plus + # validate-plan.sh, AUDIT.json, and generated AUDIT.mdx stay read-only during + # import; contract inspection runs through lib/sibling-artifacts.js. import-sibling-artifacts: tier: 0 needs: preflight @@ -49,6 +49,7 @@ jobs: uses: god-reconstructor@^1.0.0 with: prefer-imported-plan: .godplans/PLAN.mdx + require-plan-validator: .godplans/validate-plan.sh debt-assess: tier: 0 @@ -71,6 +72,7 @@ jobs: compare: existing-codebase-to-canonical-godpowers-arc compare-inputs: - .godplans/PLAN.mdx + - .godplans/validate-plan.sh - .godaudits/AUDIT.json - .godaudits/AUDIT.mdx - .godaudits/AUDIT.md diff --git a/workflows/full-arc.yaml b/workflows/full-arc.yaml index 442d135..a5b9675 100644 --- a/workflows/full-arc.yaml +++ b/workflows/full-arc.yaml @@ -25,11 +25,13 @@ on: [/god-mode] jobs: # The context job also runs sibling-artifact detection. When - # .godplans/PLAN.mdx exists in an otherwise greenfield repo - # (greenfield-with-plan), lib/planning-systems.importPlanningContext seeds + # .godplans/PLAN.mdx exists in an otherwise greenfield repo, the helper + # inspects its pinned executable .godplans/validate-plan.sh companion. + # For a greenfield-with-plan run, planning-system import seeds # tier-1 artifacts as status 'imported' before god-pm runs, so god-pm and # god-architect harden the imported plan seeds instead of interviewing from - # zero. PLAN.mdx stays read-only; parsing via lib/sibling-artifacts.js. + # zero. Incomplete or lifecycle-ineligible plans never dispatch GP work. + # Both Godplans files stay read-only outside explicit plan execution. context: tier: 0 uses: god-orchestrator@^1.0.0