@tyroneross/build-loop 0.30.3 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +2 -2
- package/.claude-plugin/marketplace.json +4 -4
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +9 -0
- package/README.md +8 -7
- package/agents/advisor.md +111 -0
- package/agents/alignment-checker.md +11 -0
- package/agents/assessment-orchestrator.md +1 -1
- package/agents/build-orchestrator.md +28 -33
- package/agents/fact-checker.md +1 -1
- package/agents/fix-critique.md +1 -1
- package/agents/independent-auditor.md +1 -1
- package/agents/overfitting-reviewer.md +1 -1
- package/agents/plan-critic.md +5 -4
- package/agents/promotion-reviewer.md +1 -1
- package/agents/retrospective-synthesizer.md +20 -3
- package/agents/scope-auditor.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/self-improvement-architect.md +1 -1
- package/commands/{handoff.md → compose-handoff.md} +4 -4
- package/commands/{optimize.md → optimize-run.md} +1 -1
- package/commands/{agent-rally-point.md → rally-point.md} +3 -3
- package/commands/{research.md → research-run.md} +1 -1
- package/commands/setup-memory.md +32 -0
- package/commands/{plan-verify.md → verify-plan.md} +1 -1
- package/docs/agent-surface-policy.md +38 -20
- package/docs/memory-setup.md +19 -14
- package/hooks/_resolve_python.sh +28 -0
- package/hooks/closeout.sh +58 -0
- package/hooks/git/pre-push +69 -6
- package/hooks/hooks.json +34 -13
- package/hooks/post-push-closeout.sh +73 -0
- package/hooks/session-start-closeout.sh +69 -0
- package/hooks/session-start-memory.sh +27 -0
- package/hooks/session-start-plugin-heal.sh +33 -0
- package/hooks/test_closeout.sh +124 -0
- package/package.json +7 -4
- package/plugin-artifacts/codex/.codex-plugin/plugin.json +41 -0
- package/plugin-artifacts/codex/AGENTS.md +499 -0
- package/plugin-artifacts/codex/BUILD-ARTIFACT.md +5 -0
- package/plugin-artifacts/codex/LICENSE +202 -0
- package/plugin-artifacts/codex/README.md +404 -0
- package/plugin-artifacts/codex/docs/agent-surface-policy.md +63 -0
- package/plugin-artifacts/codex/references/advisor-dispatch-ladder.md +62 -0
- package/plugin-artifacts/codex/references/agent-role-taxonomy.md +101 -0
- package/plugin-artifacts/codex/references/autonomous-and-per-commit-modes.md +161 -0
- package/plugin-artifacts/codex/references/autonomy-config.md +231 -0
- package/plugin-artifacts/codex/references/capability-routing.md +182 -0
- package/plugin-artifacts/codex/references/codex-subagents.md +106 -0
- package/plugin-artifacts/codex/references/coordination-file-template.md +181 -0
- package/plugin-artifacts/codex/references/coordination-rules.md +283 -0
- package/plugin-artifacts/codex/references/dogfood-reload-checkpoint.md +112 -0
- package/plugin-artifacts/codex/references/halt-and-ask-protocol.md +102 -0
- package/plugin-artifacts/codex/references/implementer-envelope-schema.md +302 -0
- package/plugin-artifacts/codex/references/intent-capability-pack.md +257 -0
- package/plugin-artifacts/codex/references/intent-exploration-prompts.md +96 -0
- package/plugin-artifacts/codex/references/leadership.md +72 -0
- package/plugin-artifacts/codex/references/memory-systems.md +238 -0
- package/plugin-artifacts/codex/references/memory.md +297 -0
- package/plugin-artifacts/codex/references/model-tier-mapping.md +257 -0
- package/plugin-artifacts/codex/references/modular-systems-pack.md +96 -0
- package/plugin-artifacts/codex/references/phase-1-assess.md +231 -0
- package/plugin-artifacts/codex/references/phase-2-plan.md +77 -0
- package/plugin-artifacts/codex/references/phase-3-execute.md +47 -0
- package/plugin-artifacts/codex/references/phase-4-review.md +320 -0
- package/plugin-artifacts/codex/references/phase-5-iterate.md +71 -0
- package/plugin-artifacts/codex/references/phase-6-learn.md +56 -0
- package/plugin-artifacts/codex/references/recent-design-structures.md +274 -0
- package/plugin-artifacts/codex/references/research-trigger-policy.md +140 -0
- package/plugin-artifacts/codex/references/runtime-smoke-triggers.md +42 -0
- package/plugin-artifacts/codex/references/self-review.md +234 -0
- package/plugin-artifacts/codex/references/single-writer-commit-protocol.md +90 -0
- package/plugin-artifacts/codex/references/task-capture-policy.md +68 -0
- package/plugin-artifacts/codex/references/ui-io-contract.md +116 -0
- package/plugin-artifacts/codex/references/ui-spotcheck-protocol.md +65 -0
- package/plugin-artifacts/codex/skills/build-loop/SKILL.md +372 -0
- package/plugin-artifacts/codex/skills/build-loop/detect-plugins.mjs +82 -0
- package/plugin-artifacts/codex/skills/build-loop/eval-guide.md +65 -0
- package/plugin-artifacts/codex/skills/build-loop/fallbacks.md +549 -0
- package/plugin-artifacts/codex/skills/build-loop/phases/fact-check.md +42 -0
- package/plugin-artifacts/codex/skills/build-loop/phases/ui-validation.md +267 -0
- package/plugin-artifacts/codex/skills/build-loop/references/advisor-dispatch-ladder.md +62 -0
- package/plugin-artifacts/codex/skills/build-loop/references/apple-native-planning.md +439 -0
- package/plugin-artifacts/codex/skills/build-loop/references/autonomous-and-per-commit-modes.md +161 -0
- package/plugin-artifacts/codex/skills/build-loop/references/capability-routing.md +182 -0
- package/plugin-artifacts/codex/skills/build-loop/references/codex-subagents.md +106 -0
- package/plugin-artifacts/codex/skills/build-loop/references/coordination.md +161 -0
- package/plugin-artifacts/codex/skills/build-loop/references/correction-aware-capture.md +177 -0
- package/plugin-artifacts/codex/skills/build-loop/references/independent-auditor.md +72 -0
- package/plugin-artifacts/codex/skills/build-loop/references/intent-capability-pack.md +257 -0
- package/plugin-artifacts/codex/skills/build-loop/references/intent-exploration-prompts.md +96 -0
- package/plugin-artifacts/codex/skills/build-loop/references/leadership.md +72 -0
- package/plugin-artifacts/codex/skills/build-loop/references/memory.md +297 -0
- package/plugin-artifacts/codex/skills/build-loop/references/modular-systems-pack.md +96 -0
- package/plugin-artifacts/codex/skills/build-loop/references/output-style.md +129 -0
- package/plugin-artifacts/codex/skills/build-loop/references/pay-it-forward-arch.md +98 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-1-assess.md +231 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-2-plan.md +77 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-3-execute.md +47 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-4-review.md +320 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-5-iterate.md +71 -0
- package/plugin-artifacts/codex/skills/build-loop/references/phase-6-learn.md +56 -0
- package/plugin-artifacts/codex/skills/build-loop/references/recent-design-structures.md +274 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
- package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
- package/plugin-artifacts/codex/skills/build-loop/references/self-recursive-dev.md +77 -0
- package/plugin-artifacts/codex/skills/build-loop/references/self-review.md +234 -0
- package/plugin-artifacts/codex/skills/build-loop/references/ui-io-contract.md +116 -0
- package/plugin-artifacts/codex/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
- package/plugin-artifacts/codex/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/backlog-item.md +35 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/codex-worker-prompt.md +96 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
- package/plugin-artifacts/codex/skills/build-loop/templates/ux-fix-plan.md +40 -0
- package/scripts/_paths.py +59 -13
- package/scripts/build_codex_plugin_artifact.py +314 -0
- package/scripts/check_cache_sync.py +1 -1
- package/scripts/install_memory.py +11 -3
- package/scripts/prune_plugin_cache.py +225 -11
- package/scripts/sync_navgator_lessons.py +25 -0
- package/scripts/sync_plugin_cache.py +66 -36
- package/skills/agent-rally-point/SKILL.md +1 -1
- package/skills/agent-rally-watcher/SKILL.md +1 -1
- package/skills/build-loop/SKILL.md +11 -155
- package/skills/build-loop/references/advisor-dispatch-ladder.md +62 -0
- package/skills/build-loop/references/autonomous-and-per-commit-modes.md +161 -0
- package/skills/build-loop/references/capability-routing.md +4 -3
- package/skills/build-loop/references/codex-subagents.md +8 -0
- package/skills/build-loop/references/coordination.md +1 -1
- package/skills/build-loop/references/intent-capability-pack.md +88 -1
- package/skills/build-loop/references/memory.md +17 -2
- package/skills/build-loop/references/phase-1-assess.md +13 -0
- package/skills/build-loop/references/phase-2-plan.md +1 -1
- package/skills/build-loop/references/phase-4-review.md +12 -0
- package/skills/build-loop/references/phase-6-learn.md +1 -1
- package/skills/build-loop/references/self-review.md +4 -2
- package/skills/build-loop/templates/codex-worker-prompt.md +41 -34
- package/skills/handoff/SKILL.md +3 -3
- package/skills/loop-builder/SKILL.md +98 -0
- package/skills/loop-builder/presets/active-project-evidence.yaml +97 -0
- package/skills/loop-builder/presets/generic-artifact-loop.yaml +95 -0
- package/skills/loop-builder/presets/presentation-audit.yaml +96 -0
- package/skills/loop-builder/presets/research-synthesis.yaml +96 -0
- package/skills/loop-builder/presets/source-ingestion-raw-data-audit.yaml +96 -0
- package/skills/loop-builder/references/spec-format.md +80 -0
- package/skills/loop-builder/scripts/loop_builder.py +346 -0
- package/skills/model-tiering/SKILL.md +65 -40
- package/skills/optimize/SKILL.md +2 -2
- package/skills/plan-verify/SKILL.md +1 -1
- package/skills/plugin-builder/references/distribution.md +8 -0
- package/skills/research/SKILL.md +33 -6
- package/skills/runtime-parity-verification/SKILL.md +51 -0
- package/skills/spec-writing/SKILL.md +19 -0
- package/templates/memory/README.md +65 -0
- package/templates/memory/charter.md.template +46 -0
- package/templates/memory/manifest.json +24 -4
- package/codex-skills/debug-loop/SKILL.md +0 -18
- package/codex-skills/handoff/SKILL.md +0 -22
- package/codex-skills/knowledge/SKILL.md +0 -21
- package/codex-skills/optimize/SKILL.md +0 -18
- package/codex-skills/research/SKILL.md +0 -18
- /package/commands/{knowledge-review.md → review-knowledge.md} +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "research-synthesis",
|
|
3
|
+
"title": "Research Synthesis Loop",
|
|
4
|
+
"summary": "Answer a question with explicit source strategy, evidence extraction, confidence, and durable citations.",
|
|
5
|
+
"inputs": [
|
|
6
|
+
"research question",
|
|
7
|
+
"decision use",
|
|
8
|
+
"freshness requirement",
|
|
9
|
+
"candidate sources",
|
|
10
|
+
"source quality rubric"
|
|
11
|
+
],
|
|
12
|
+
"outputs": [
|
|
13
|
+
"source strategy",
|
|
14
|
+
"evidence table",
|
|
15
|
+
"synthesis memo",
|
|
16
|
+
"claim verification log",
|
|
17
|
+
"reusable search strategy"
|
|
18
|
+
],
|
|
19
|
+
"phases": {
|
|
20
|
+
"assess": {
|
|
21
|
+
"goal": "Frame the question, scope, decision use, freshness need, and inclusion/exclusion rules.",
|
|
22
|
+
"required": ["question", "decision_use", "freshness_requirement", "inclusion_exclusion"]
|
|
23
|
+
},
|
|
24
|
+
"plan": {
|
|
25
|
+
"goal": "Plan source collection, extraction fields, and confidence markers.",
|
|
26
|
+
"required": ["source_strategy", "extraction_schema", "confidence_scheme"]
|
|
27
|
+
},
|
|
28
|
+
"produce": {
|
|
29
|
+
"goal": "Collect sources, extract evidence, and synthesize the answer.",
|
|
30
|
+
"required": ["source_list", "evidence_table", "synthesis"]
|
|
31
|
+
},
|
|
32
|
+
"review": {
|
|
33
|
+
"goal": "Verify claims, dates, statistics, and contested points.",
|
|
34
|
+
"required": ["claim_inventory", "verification_log", "weak_claims"]
|
|
35
|
+
},
|
|
36
|
+
"learn": {
|
|
37
|
+
"goal": "Persist the report, source quirks, and reusable search strategy.",
|
|
38
|
+
"required": ["saved_report", "search_strategy", "source_quirks"]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"validators": [
|
|
42
|
+
{
|
|
43
|
+
"id": "claim_citation",
|
|
44
|
+
"pass_condition": "Every material claim has a citation or is marked inferred/unsupported.",
|
|
45
|
+
"method": "review"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "freshness_trace",
|
|
49
|
+
"pass_condition": "Time-sensitive claims include source publication or capture date.",
|
|
50
|
+
"method": "review"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "source_quality",
|
|
54
|
+
"pass_condition": "Each source has a quality tier and role in the answer.",
|
|
55
|
+
"method": "review"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "weak_claim_handling",
|
|
59
|
+
"pass_condition": "Unsupported claims are removed, downgraded, or labeled explicitly.",
|
|
60
|
+
"method": "review"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"gates": {
|
|
64
|
+
"confirm": ["external_send", "legal_or_compliance_assertion", "sensitive_data_exposure"],
|
|
65
|
+
"surface": ["people_impacting_decision", "money_movement"]
|
|
66
|
+
},
|
|
67
|
+
"skill_chain": {
|
|
68
|
+
"intake": {
|
|
69
|
+
"primary": ["research"],
|
|
70
|
+
"optional": [],
|
|
71
|
+
"fallback": "Use local repo/wiki scan and explicit source list.",
|
|
72
|
+
"handoff_artifact": "research_packet.md"
|
|
73
|
+
},
|
|
74
|
+
"produce": {
|
|
75
|
+
"primary": ["research"],
|
|
76
|
+
"optional": ["pyramid-principle:pyramid-short-form"],
|
|
77
|
+
"fallback": "Draft synthesis from evidence table with confidence markers.",
|
|
78
|
+
"handoff_artifact": "synthesis_memo.md"
|
|
79
|
+
},
|
|
80
|
+
"review": {
|
|
81
|
+
"primary": ["research"],
|
|
82
|
+
"optional": [],
|
|
83
|
+
"fallback": "Run claim inventory and citation checks manually.",
|
|
84
|
+
"handoff_artifact": "claim_verification_log.md"
|
|
85
|
+
},
|
|
86
|
+
"learn": {
|
|
87
|
+
"primary": ["build-loop:knowledge"],
|
|
88
|
+
"optional": [],
|
|
89
|
+
"fallback": "Persist source quirks and search strategy in the report.",
|
|
90
|
+
"handoff_artifact": "research_lessons.md"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"learn": {
|
|
94
|
+
"store": ["search_strategy", "source_quirks", "claim_failures", "useful_sources"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "source-ingestion-raw-data-audit",
|
|
3
|
+
"title": "Source Ingestion and Raw Data Audit Loop",
|
|
4
|
+
"summary": "Inventory, dedupe, preserve, and audit source artifacts before promoting claims or source cards.",
|
|
5
|
+
"inputs": [
|
|
6
|
+
"source folder",
|
|
7
|
+
"candidate files",
|
|
8
|
+
"spreadsheets or survey exports",
|
|
9
|
+
"artifact versions",
|
|
10
|
+
"project question"
|
|
11
|
+
],
|
|
12
|
+
"outputs": [
|
|
13
|
+
"source inventory",
|
|
14
|
+
"duplicate and version map",
|
|
15
|
+
"raw-data manifest",
|
|
16
|
+
"ingestion priority list",
|
|
17
|
+
"source-card drafting queue"
|
|
18
|
+
],
|
|
19
|
+
"phases": {
|
|
20
|
+
"assess": {
|
|
21
|
+
"goal": "Identify source roots, sensitive material, project questions, and raw-data-bearing files.",
|
|
22
|
+
"required": ["source_roots", "sensitivity_labels", "project_questions", "raw_data_candidates"]
|
|
23
|
+
},
|
|
24
|
+
"plan": {
|
|
25
|
+
"goal": "Prioritize ingestion by decision relevance, source quality, and dedupe risk.",
|
|
26
|
+
"required": ["ingestion_order", "duplicate_strategy", "raw_preservation_strategy"]
|
|
27
|
+
},
|
|
28
|
+
"produce": {
|
|
29
|
+
"goal": "Write inventory, duplicate map, raw-data manifest, and source-card queue.",
|
|
30
|
+
"required": ["hashes", "row_column_counts", "sheet_names", "filters_or_denominators"]
|
|
31
|
+
},
|
|
32
|
+
"review": {
|
|
33
|
+
"goal": "Verify version choices and that quantitative claims can trace to raw or surrogate data.",
|
|
34
|
+
"required": ["version_verdicts", "raw_trace_checks", "uncertain_items"]
|
|
35
|
+
},
|
|
36
|
+
"learn": {
|
|
37
|
+
"goal": "Capture source quirks and future ingestion rules.",
|
|
38
|
+
"required": ["source_quirks", "reusable_ingestion_rules"]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"validators": [
|
|
42
|
+
{
|
|
43
|
+
"id": "source_inventory_complete",
|
|
44
|
+
"pass_condition": "Every candidate source has path, type, size or count proxy, and ingestion status.",
|
|
45
|
+
"method": "code-or-review"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "duplicate_resolution",
|
|
49
|
+
"pass_condition": "Same-title or same-hash files are grouped with an explicit keep/compare/defer verdict.",
|
|
50
|
+
"method": "review"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "raw_data_trace",
|
|
54
|
+
"pass_condition": "Every quantitative source links to an original file or stable surrogate with sheet/table names and row/column counts.",
|
|
55
|
+
"method": "code-or-review"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "uncertain_items_flagged",
|
|
59
|
+
"pass_condition": "Uncertain, inaccessible, or sensitive items are listed instead of silently skipped.",
|
|
60
|
+
"method": "review"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"gates": {
|
|
64
|
+
"confirm": ["sensitive_data_exposure", "irreversible_source_change"],
|
|
65
|
+
"surface": ["legal_or_compliance_assertion"]
|
|
66
|
+
},
|
|
67
|
+
"skill_chain": {
|
|
68
|
+
"intake": {
|
|
69
|
+
"primary": ["research"],
|
|
70
|
+
"optional": ["doc"],
|
|
71
|
+
"fallback": "Run filesystem inventory and summarize file types manually.",
|
|
72
|
+
"handoff_artifact": "source_inventory.md"
|
|
73
|
+
},
|
|
74
|
+
"produce": {
|
|
75
|
+
"primary": [],
|
|
76
|
+
"optional": ["research"],
|
|
77
|
+
"fallback": "Use deterministic scripts or shell inventory for hashes and counts.",
|
|
78
|
+
"handoff_artifact": "raw_data_manifest.md"
|
|
79
|
+
},
|
|
80
|
+
"review": {
|
|
81
|
+
"primary": ["research"],
|
|
82
|
+
"optional": [],
|
|
83
|
+
"fallback": "Review duplicate/version choices against file metadata and source content.",
|
|
84
|
+
"handoff_artifact": "ingestion_review.md"
|
|
85
|
+
},
|
|
86
|
+
"learn": {
|
|
87
|
+
"primary": ["build-loop:knowledge"],
|
|
88
|
+
"optional": [],
|
|
89
|
+
"fallback": "Append durable ingestion lessons to the report.",
|
|
90
|
+
"handoff_artifact": "ingestion_lessons.md"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"learn": {
|
|
94
|
+
"store": ["source_quirks", "dedupe_rules", "raw_data_rules", "uncertain_item_patterns"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Loop Spec Format
|
|
2
|
+
|
|
3
|
+
<!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
|
|
4
|
+
|
|
5
|
+
A focused-loop spec is the domain contract for a reusable workflow. The runner owns phase movement, validation order, iteration, gates, queues, and learn/memory. The spec owns the artifact vocabulary.
|
|
6
|
+
|
|
7
|
+
## Required Fields
|
|
8
|
+
|
|
9
|
+
| Field | Purpose |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `schema_version` | Loop spec version. Start at `1`. |
|
|
12
|
+
| `id` | Kebab-case loop identifier. |
|
|
13
|
+
| `title` | Human-readable loop name. |
|
|
14
|
+
| `summary` | One-sentence purpose. |
|
|
15
|
+
| `inputs` | Accepted source materials or state surfaces. |
|
|
16
|
+
| `outputs` | Artifacts the loop produces. |
|
|
17
|
+
| `phases` | Domain-specific work contract per phase. |
|
|
18
|
+
| `validators` | Pass/fail checks used during review. |
|
|
19
|
+
| `gates` | Human confirmation boundaries. |
|
|
20
|
+
| `skill_chain` | Optional phase-to-skill routing plan. |
|
|
21
|
+
| `learn` | Durable reuse payload. |
|
|
22
|
+
|
|
23
|
+
## Skill Chain Shape
|
|
24
|
+
|
|
25
|
+
`skill_chain` is a plan for chaining specialized skills. It is advisory unless the runner or host can prove the named skill exists.
|
|
26
|
+
|
|
27
|
+
Each phase may declare:
|
|
28
|
+
|
|
29
|
+
| Field | Purpose |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `primary` | Preferred skills for this phase. |
|
|
32
|
+
| `optional` | Skills to use when source type or scope warrants them. |
|
|
33
|
+
| `fallback` | What to do if the skill is unavailable. |
|
|
34
|
+
| `handoff_artifact` | Required output from the chained skill. |
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
skill_chain:
|
|
40
|
+
intake:
|
|
41
|
+
primary:
|
|
42
|
+
- research
|
|
43
|
+
optional:
|
|
44
|
+
- doc
|
|
45
|
+
fallback: "Manual source inventory with cited file paths."
|
|
46
|
+
handoff_artifact: "source_inventory.md"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Gate Rules
|
|
50
|
+
|
|
51
|
+
Keep common gates centralized. Loop specs should name examples, not redefine policy.
|
|
52
|
+
|
|
53
|
+
Standard confirmation gates:
|
|
54
|
+
|
|
55
|
+
- external_send
|
|
56
|
+
- sensitive_data_exposure
|
|
57
|
+
- money_movement
|
|
58
|
+
- legal_or_compliance_assertion
|
|
59
|
+
- production_or_customer_operation
|
|
60
|
+
- people_impacting_decision
|
|
61
|
+
- irreversible_source_change
|
|
62
|
+
|
|
63
|
+
## Validator Rules
|
|
64
|
+
|
|
65
|
+
Validators should be binary. Avoid vague scores. If the check requires judgment, phrase it as a pass/fail rubric with named evidence.
|
|
66
|
+
|
|
67
|
+
Good:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
- id: source_trace
|
|
71
|
+
pass_condition: "Every material claim cites a source path, slide, row, transcript timestamp, or explicitly marked assumption."
|
|
72
|
+
method: "review"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Weak:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
- id: quality
|
|
79
|
+
pass_condition: "The artifact is good."
|
|
80
|
+
```
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
"""Generate focused-loop packs from preset specs.
|
|
5
|
+
|
|
6
|
+
Preset files use YAML-compatible JSON so this tool stays stdlib-only while the
|
|
7
|
+
generated loop spec remains readable YAML.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import copy
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import shutil
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
HERE = Path(__file__).resolve().parent
|
|
21
|
+
SKILL_ROOT = HERE.parent
|
|
22
|
+
PRESETS_DIR = SKILL_ROOT / "presets"
|
|
23
|
+
|
|
24
|
+
IDENTIFIER_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LoopBuilderError(RuntimeError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def slugify(value: str) -> str:
|
|
32
|
+
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
33
|
+
slug = re.sub(r"-+", "-", slug)
|
|
34
|
+
if not slug:
|
|
35
|
+
raise LoopBuilderError("loop id cannot be empty")
|
|
36
|
+
return slug
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def require_identifier(value: str, *, field: str) -> str:
|
|
40
|
+
slug = slugify(value)
|
|
41
|
+
if not IDENTIFIER_RE.match(slug):
|
|
42
|
+
raise LoopBuilderError(f"{field} must be kebab-case: {value!r}")
|
|
43
|
+
return slug
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def preset_paths() -> list[Path]:
|
|
47
|
+
return sorted(PRESETS_DIR.glob("*.yaml"))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def available_presets() -> list[str]:
|
|
51
|
+
return [path.stem for path in preset_paths()]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_preset(name: str) -> dict[str, Any]:
|
|
55
|
+
preset_id = require_identifier(name, field="preset")
|
|
56
|
+
path = PRESETS_DIR / f"{preset_id}.yaml"
|
|
57
|
+
if not path.is_file():
|
|
58
|
+
choices = ", ".join(available_presets()) or "(none)"
|
|
59
|
+
raise LoopBuilderError(f"unknown preset {preset_id!r}; available: {choices}")
|
|
60
|
+
try:
|
|
61
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
62
|
+
except json.JSONDecodeError as exc:
|
|
63
|
+
raise LoopBuilderError(f"invalid preset JSON in {path}: {exc}") from exc
|
|
64
|
+
validate_preset(data, source=path)
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def validate_preset(data: dict[str, Any], *, source: Path | None = None) -> None:
|
|
69
|
+
required = ("id", "title", "summary", "inputs", "outputs", "phases", "validators", "gates", "skill_chain", "learn")
|
|
70
|
+
missing = [key for key in required if key not in data]
|
|
71
|
+
if missing:
|
|
72
|
+
label = str(source) if source else data.get("id", "<preset>")
|
|
73
|
+
raise LoopBuilderError(f"{label} missing required fields: {', '.join(missing)}")
|
|
74
|
+
require_identifier(str(data["id"]), field="preset id")
|
|
75
|
+
if not isinstance(data["phases"], dict) or not data["phases"]:
|
|
76
|
+
raise LoopBuilderError(f"{data['id']} phases must be a non-empty object")
|
|
77
|
+
if not isinstance(data["validators"], list) or not data["validators"]:
|
|
78
|
+
raise LoopBuilderError(f"{data['id']} validators must be a non-empty list")
|
|
79
|
+
if not isinstance(data["skill_chain"], dict):
|
|
80
|
+
raise LoopBuilderError(f"{data['id']} skill_chain must be an object")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def scalar_to_yaml(value: Any) -> str:
|
|
84
|
+
if value is True:
|
|
85
|
+
return "true"
|
|
86
|
+
if value is False:
|
|
87
|
+
return "false"
|
|
88
|
+
if value is None:
|
|
89
|
+
return "null"
|
|
90
|
+
if isinstance(value, (int, float)):
|
|
91
|
+
return str(value)
|
|
92
|
+
return json.dumps(str(value), ensure_ascii=False)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def to_yaml(value: Any, indent: int = 0) -> str:
|
|
96
|
+
pad = " " * indent
|
|
97
|
+
if isinstance(value, dict):
|
|
98
|
+
if not value:
|
|
99
|
+
return pad + "{}"
|
|
100
|
+
lines: list[str] = []
|
|
101
|
+
for key, item in value.items():
|
|
102
|
+
if isinstance(item, (dict, list)):
|
|
103
|
+
if not item:
|
|
104
|
+
lines.append(f"{pad}{key}: {to_yaml(item, 0).strip()}")
|
|
105
|
+
else:
|
|
106
|
+
lines.append(f"{pad}{key}:")
|
|
107
|
+
lines.append(to_yaml(item, indent + 2))
|
|
108
|
+
else:
|
|
109
|
+
lines.append(f"{pad}{key}: {scalar_to_yaml(item)}")
|
|
110
|
+
return "\n".join(lines)
|
|
111
|
+
if isinstance(value, list):
|
|
112
|
+
if not value:
|
|
113
|
+
return pad + "[]"
|
|
114
|
+
lines = []
|
|
115
|
+
for item in value:
|
|
116
|
+
if isinstance(item, (dict, list)):
|
|
117
|
+
lines.append(f"{pad}-")
|
|
118
|
+
lines.append(to_yaml(item, indent + 2))
|
|
119
|
+
else:
|
|
120
|
+
lines.append(f"{pad}- {scalar_to_yaml(item)}")
|
|
121
|
+
return "\n".join(lines)
|
|
122
|
+
return pad + scalar_to_yaml(value)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def build_loop_spec(preset: dict[str, Any], *, loop_id: str, title: str | None, preset_name: str) -> dict[str, Any]:
|
|
126
|
+
spec = copy.deepcopy(preset)
|
|
127
|
+
spec["schema_version"] = 1
|
|
128
|
+
spec["id"] = loop_id
|
|
129
|
+
spec["source_preset"] = preset_name
|
|
130
|
+
if title:
|
|
131
|
+
spec["title"] = title
|
|
132
|
+
return spec
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def render_loop_yaml(spec: dict[str, Any]) -> str:
|
|
136
|
+
header = [
|
|
137
|
+
"# Generated focused-loop spec.",
|
|
138
|
+
"# Edit the artifact contract, validators, and skill_chain before using on high-stakes work.",
|
|
139
|
+
"",
|
|
140
|
+
]
|
|
141
|
+
return "\n".join(header) + to_yaml(spec) + "\n"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def render_rubric(spec: dict[str, Any]) -> str:
|
|
145
|
+
lines = [
|
|
146
|
+
f"# {spec['title']} Rubric",
|
|
147
|
+
"",
|
|
148
|
+
spec["summary"],
|
|
149
|
+
"",
|
|
150
|
+
"## Binary Validators",
|
|
151
|
+
"",
|
|
152
|
+
"| Validator | Pass Condition | Method |",
|
|
153
|
+
"|---|---|---|",
|
|
154
|
+
]
|
|
155
|
+
for validator in spec["validators"]:
|
|
156
|
+
lines.append(
|
|
157
|
+
f"| `{validator['id']}` | {validator['pass_condition']} | {validator.get('method', 'review')} |"
|
|
158
|
+
)
|
|
159
|
+
lines.extend(
|
|
160
|
+
[
|
|
161
|
+
"",
|
|
162
|
+
"## Confirmation Gates",
|
|
163
|
+
"",
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
for gate_name, gate_items in spec.get("gates", {}).items():
|
|
167
|
+
lines.append(f"### {gate_name}")
|
|
168
|
+
lines.append("")
|
|
169
|
+
for item in gate_items:
|
|
170
|
+
lines.append(f"- `{item}`")
|
|
171
|
+
lines.append("")
|
|
172
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def render_report_template(spec: dict[str, Any]) -> str:
|
|
176
|
+
validator_rows = "\n".join(f"| `{item['id']}` | pass/fail | evidence |" for item in spec["validators"])
|
|
177
|
+
outputs = "\n".join(f"- {item}" for item in spec["outputs"])
|
|
178
|
+
return f"""# {spec['title']} Report
|
|
179
|
+
|
|
180
|
+
## Bottom Line
|
|
181
|
+
|
|
182
|
+
[One sentence: what changed, what decision/artifact is ready, and what remains open.]
|
|
183
|
+
|
|
184
|
+
## Outputs
|
|
185
|
+
|
|
186
|
+
{outputs}
|
|
187
|
+
|
|
188
|
+
## Evidence
|
|
189
|
+
|
|
190
|
+
- [source path / transcript timestamp / row or slide reference]
|
|
191
|
+
|
|
192
|
+
## Validator Results
|
|
193
|
+
|
|
194
|
+
| Validator | Verdict | Evidence |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
{validator_rows}
|
|
197
|
+
|
|
198
|
+
## Gates
|
|
199
|
+
|
|
200
|
+
- External send/publish: [clear/confirm]
|
|
201
|
+
- Sensitive data exposure: [clear/confirm]
|
|
202
|
+
- Irreversible source-of-truth change: [clear/confirm]
|
|
203
|
+
|
|
204
|
+
## Learn
|
|
205
|
+
|
|
206
|
+
- Reusable artifact:
|
|
207
|
+
- Source quirks:
|
|
208
|
+
- Rubric failures:
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def render_validator(spec: dict[str, Any]) -> str:
|
|
213
|
+
required_tokens = ["schema_version", "id", "phases", "validators", "gates", "skill_chain", "learn"]
|
|
214
|
+
token_literal = repr(required_tokens)
|
|
215
|
+
return f'''#!/usr/bin/env python3
|
|
216
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
217
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
218
|
+
"""Basic generated validator for the {spec["id"]} loop pack."""
|
|
219
|
+
from __future__ import annotations
|
|
220
|
+
|
|
221
|
+
import sys
|
|
222
|
+
from pathlib import Path
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
226
|
+
REQUIRED_FILES = [
|
|
227
|
+
ROOT / "loop.yaml",
|
|
228
|
+
ROOT / "rubric.md",
|
|
229
|
+
ROOT / "templates" / "report.md",
|
|
230
|
+
]
|
|
231
|
+
REQUIRED_TOKENS = {token_literal}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def main() -> int:
|
|
235
|
+
missing = [str(path.relative_to(ROOT)) for path in REQUIRED_FILES if not path.is_file()]
|
|
236
|
+
if missing:
|
|
237
|
+
print("missing required files: " + ", ".join(missing), file=sys.stderr)
|
|
238
|
+
return 1
|
|
239
|
+
text = (ROOT / "loop.yaml").read_text(encoding="utf-8")
|
|
240
|
+
absent = [token for token in REQUIRED_TOKENS if token + ":" not in text]
|
|
241
|
+
if absent:
|
|
242
|
+
print("loop.yaml missing required fields: " + ", ".join(absent), file=sys.stderr)
|
|
243
|
+
return 1
|
|
244
|
+
print("loop pack ok: " + str(ROOT))
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
if __name__ == "__main__":
|
|
249
|
+
raise SystemExit(main())
|
|
250
|
+
'''
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def write_file(path: Path, content: str, *, executable: bool = False) -> None:
|
|
254
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
255
|
+
path.write_text(content, encoding="utf-8")
|
|
256
|
+
if executable:
|
|
257
|
+
path.chmod(0o755)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def create_loop(
|
|
261
|
+
*,
|
|
262
|
+
loop_id_raw: str,
|
|
263
|
+
preset_name: str,
|
|
264
|
+
output: Path | None,
|
|
265
|
+
title: str | None,
|
|
266
|
+
force: bool,
|
|
267
|
+
) -> Path:
|
|
268
|
+
loop_id = require_identifier(loop_id_raw, field="loop id")
|
|
269
|
+
preset_id = require_identifier(preset_name, field="preset")
|
|
270
|
+
preset = load_preset(preset_id)
|
|
271
|
+
spec = build_loop_spec(preset, loop_id=loop_id, title=title, preset_name=preset_id)
|
|
272
|
+
target = output.expanduser().resolve() if output else (Path.cwd() / ".build-loop" / "loops" / loop_id).resolve()
|
|
273
|
+
if target.exists():
|
|
274
|
+
if not force:
|
|
275
|
+
raise LoopBuilderError(f"target already exists: {target}; pass --force to replace")
|
|
276
|
+
shutil.rmtree(target)
|
|
277
|
+
write_file(target / "loop.yaml", render_loop_yaml(spec))
|
|
278
|
+
write_file(target / "rubric.md", render_rubric(spec))
|
|
279
|
+
write_file(target / "templates" / "report.md", render_report_template(spec))
|
|
280
|
+
write_file(target / "validators" / "validate_loop.py", render_validator(spec), executable=True)
|
|
281
|
+
write_file(
|
|
282
|
+
target / "README.md",
|
|
283
|
+
f"# {spec['title']}\n\nGenerated from `{preset_id}`. Start with `loop.yaml`, then run `python3 validators/validate_loop.py`.\n",
|
|
284
|
+
)
|
|
285
|
+
return target
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def cmd_list(_args: argparse.Namespace) -> int:
|
|
289
|
+
for name in available_presets():
|
|
290
|
+
preset = load_preset(name)
|
|
291
|
+
print(f"{name}\t{preset['title']}")
|
|
292
|
+
return 0
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def cmd_inspect(args: argparse.Namespace) -> int:
|
|
296
|
+
preset = load_preset(args.preset)
|
|
297
|
+
print(render_loop_yaml(build_loop_spec(preset, loop_id=preset["id"], title=None, preset_name=preset["id"])))
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def cmd_create(args: argparse.Namespace) -> int:
|
|
302
|
+
target = create_loop(
|
|
303
|
+
loop_id_raw=args.loop_id,
|
|
304
|
+
preset_name=args.preset,
|
|
305
|
+
output=Path(args.output) if args.output else None,
|
|
306
|
+
title=args.title,
|
|
307
|
+
force=args.force,
|
|
308
|
+
)
|
|
309
|
+
print(f"created loop pack: {target}")
|
|
310
|
+
return 0
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
314
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
315
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
316
|
+
|
|
317
|
+
list_parser = sub.add_parser("list", help="List available loop presets.")
|
|
318
|
+
list_parser.set_defaults(func=cmd_list)
|
|
319
|
+
|
|
320
|
+
inspect_parser = sub.add_parser("inspect", help="Print a preset as generated loop YAML.")
|
|
321
|
+
inspect_parser.add_argument("preset")
|
|
322
|
+
inspect_parser.set_defaults(func=cmd_inspect)
|
|
323
|
+
|
|
324
|
+
create_parser = sub.add_parser("create", help="Create a focused-loop pack.")
|
|
325
|
+
create_parser.add_argument("loop_id")
|
|
326
|
+
create_parser.add_argument("--preset", required=True, help="Preset name from `list`.")
|
|
327
|
+
create_parser.add_argument("--output", help="Target directory. Defaults to .build-loop/loops/<loop-id>.")
|
|
328
|
+
create_parser.add_argument("--title", help="Override generated title.")
|
|
329
|
+
create_parser.add_argument("--force", action="store_true", help="Replace an existing target directory.")
|
|
330
|
+
create_parser.set_defaults(func=cmd_create)
|
|
331
|
+
|
|
332
|
+
return parser
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def main(argv: list[str] | None = None) -> int:
|
|
336
|
+
parser = build_parser()
|
|
337
|
+
args = parser.parse_args(argv)
|
|
338
|
+
try:
|
|
339
|
+
return int(args.func(args))
|
|
340
|
+
except LoopBuilderError as exc:
|
|
341
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
342
|
+
return 1
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
if __name__ == "__main__":
|
|
346
|
+
raise SystemExit(main())
|