@zalom/plastic 2.0.0-alpha.27 → 2.0.0-alpha.28
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/PLASTIC.md +13 -139
- package/README.md +337 -133
- package/agents/plastic-enforcer.md +9 -10
- package/agents/plastic-executor.md +1 -1
- package/bin/crap +4 -0
- package/bin/lib/context_budget.rb +35 -1
- package/bin/lib/skill_census.rb +839 -0
- package/bin/plastic +6 -0
- package/bin/plastic-skill-census +114 -0
- package/bin/verify-change +345 -0
- package/deprecations.yml +1 -1
- package/{skills/agent-advisor/references → docs/help}/advisor-protocol.md +4 -7
- package/{skills/auto/references → docs/help}/agent-architecture.md +7 -7
- package/{skills/conventions/references → docs/help}/completion-and-done.md +1 -1
- package/{skills/auto/references → docs/help}/human-report-contract.md +17 -19
- package/{skills/conventions/references → docs/help}/roadmaps.md +2 -2
- package/{skills/tutorial/references → docs/help}/track-1-guided.md +8 -8
- package/{skills/tutorial/references → docs/help}/track-2-auto.md +5 -5
- package/{skills/tutorial/references → docs/help}/track-3-projects-and-roadmaps.md +24 -17
- package/package.json +3 -2
- package/scripts/append-ledger +2 -1
- package/scripts/dashboard.rb +10 -9
- package/scripts/day-summary +2 -1
- package/scripts/doctor.rb +48 -42
- package/scripts/end-intent +2 -8
- package/scripts/file-session-intent +2 -1
- package/scripts/hook-capture +4 -3
- package/scripts/hook-close +2 -1
- package/scripts/hook-record +3 -2
- package/scripts/hook-savepoint +3 -2
- package/scripts/hook-session-start +5 -4
- package/scripts/hook-stop +2 -1
- package/scripts/insight-append +1 -2
- package/scripts/install.rb +3 -1
- package/scripts/lib/active_delivery.rb +1 -1
- package/scripts/lib/arm.rb +2 -1
- package/scripts/lib/backup.rb +63 -0
- package/scripts/lib/cli/command.rb +85 -0
- package/scripts/lib/cli/commands/auto.rb +18 -0
- package/scripts/lib/cli/commands/auto_brief.rb +44 -0
- package/scripts/lib/cli/commands/auto_lock.rb +60 -0
- package/scripts/lib/cli/commands/auto_report.rb +50 -0
- package/scripts/lib/cli/commands/auto_take.rb +25 -0
- package/scripts/lib/cli/commands/backup.rb +43 -0
- package/scripts/lib/cli/commands/checkout.rb +25 -0
- package/scripts/lib/cli/commands/continue.rb +66 -0
- package/scripts/lib/cli/commands/doctor.rb +20 -0
- package/scripts/lib/cli/commands/feedback.rb +40 -0
- package/scripts/lib/cli/commands/help.rb +69 -0
- package/scripts/lib/cli/commands/hook.rb +32 -0
- package/scripts/lib/cli/commands/index.rb +23 -0
- package/scripts/lib/cli/commands/install.rb +21 -0
- package/scripts/lib/cli/commands/installer_verb.rb +37 -0
- package/scripts/lib/cli/commands/intent.rb +19 -0
- package/scripts/lib/cli/commands/intent_answer.rb +37 -0
- package/scripts/lib/cli/commands/intent_command.rb +53 -0
- package/scripts/lib/cli/commands/intent_end.rb +61 -0
- package/scripts/lib/cli/commands/intent_new.rb +62 -0
- package/scripts/lib/cli/commands/intent_note.rb +43 -0
- package/scripts/lib/cli/commands/intent_rule.rb +36 -0
- package/scripts/lib/cli/commands/intent_show.rb +25 -0
- package/scripts/lib/cli/commands/intent_spec.rb +44 -0
- package/scripts/lib/cli/commands/intent_step.rb +43 -0
- package/scripts/lib/cli/commands/intent_verify.rb +26 -0
- package/scripts/lib/cli/commands/migrate.rb +16 -0
- package/scripts/lib/cli/commands/migrate_stores.rb +31 -0
- package/scripts/lib/cli/commands/next.rb +49 -0
- package/scripts/lib/cli/commands/project.rb +19 -0
- package/scripts/lib/cli/commands/project_links.rb +42 -0
- package/scripts/lib/cli/commands/project_list.rb +20 -0
- package/scripts/lib/cli/commands/project_new.rb +72 -0
- package/scripts/lib/cli/commands/query.rb +31 -0
- package/scripts/lib/cli/commands/render.rb +25 -0
- package/scripts/lib/cli/commands/roadmap.rb +19 -0
- package/scripts/lib/cli/commands/roadmap_check.rb +44 -0
- package/scripts/lib/cli/commands/roadmap_log.rb +54 -0
- package/scripts/lib/cli/commands/roadmap_next.rb +34 -0
- package/scripts/lib/cli/commands/roadmap_show.rb +45 -0
- package/scripts/lib/cli/commands/rollback.rb +20 -0
- package/scripts/lib/cli/commands/search.rb +60 -0
- package/scripts/lib/cli/commands/session.rb +18 -0
- package/scripts/lib/cli/commands/session_commit.rb +42 -0
- package/scripts/lib/cli/commands/session_handoff.rb +34 -0
- package/scripts/lib/cli/commands/session_summary.rb +35 -0
- package/scripts/lib/cli/commands/status.rb +68 -0
- package/scripts/lib/cli/commands/subcommand_list.rb +36 -0
- package/scripts/lib/cli/commands/sync.rb +46 -0
- package/scripts/lib/cli/commands/uninstall.rb +20 -0
- package/scripts/lib/cli/commands/update.rb +20 -0
- package/scripts/lib/cli/commands/version.rb +53 -0
- package/scripts/lib/cli/frontier.rb +84 -0
- package/scripts/lib/cli/legacy.rb +50 -0
- package/scripts/lib/cli/output.rb +102 -0
- package/scripts/lib/cli/scope.rb +127 -0
- package/scripts/lib/cli/table.rb +64 -0
- package/scripts/lib/cli.rb +94 -0
- package/scripts/lib/compact_instructions.rb +8 -0
- package/scripts/lib/day_summary.rb +4 -3
- package/scripts/lib/doctor_core.rb +7 -32
- package/scripts/lib/doctor_session_ledger.rb +2 -1
- package/scripts/lib/feedback_report.rb +1 -1
- package/scripts/lib/graph_measure_models.rb +3 -1
- package/scripts/lib/index_entry.rb +9 -0
- package/scripts/lib/installer_core.rb +73 -19
- package/scripts/lib/intent_screen.rb +3 -3
- package/scripts/lib/lock.rb +2 -2
- package/scripts/lib/node_input.rb +3 -2
- package/scripts/lib/preflight.rb +2 -4
- package/scripts/lib/project_config.rb +2 -1
- package/scripts/lib/project_validator.rb +3 -2
- package/scripts/lib/qmd_sync.rb +8 -7
- package/scripts/lib/reference_archive.rb +45 -0
- package/scripts/lib/release_guard.rb +2 -0
- package/scripts/lib/report_screen.rb +4 -3
- package/scripts/lib/rlm/corpus.rb +13 -0
- package/scripts/lib/rlm/probe.rb +29 -0
- package/scripts/lib/rlm/query.rb +22 -0
- package/scripts/lib/roadmap_queue.rb +2 -2
- package/scripts/lib/roadmap_savepoint.rb +1 -1
- package/scripts/lib/runner_absorb.rb +3 -2
- package/scripts/lib/search_index.rb +55 -0
- package/scripts/lib/session_git.rb +4 -3
- package/scripts/lib/sqlite.rb +22 -0
- package/scripts/lib/store_discovery.rb +7 -6
- package/scripts/lib/store_layout.rb +54 -0
- package/scripts/lib/store_provisioning.rb +2 -1
- package/scripts/lib/store_sync.rb +85 -0
- package/scripts/lib/stores_move.rb +93 -0
- package/scripts/lib/verify_intent.rb +2 -7
- package/scripts/lib/version_number.rb +48 -0
- package/scripts/lib/work_graph.rb +59 -0
- package/scripts/lib/worktree.rb +3 -8
- package/scripts/lib/worktree_sweep.rb +3 -2
- package/scripts/link-suggest +2 -1
- package/scripts/migrate-to-global +1 -1
- package/scripts/new-intent +3 -12
- package/scripts/plastic-lock +3 -2
- package/scripts/promote-session-item +3 -2
- package/scripts/release-check +10 -5
- package/scripts/report-screen +1 -1
- package/scripts/session-commit +2 -1
- package/scripts/spawn-preamble +2 -2
- package/scripts/update.rb +25 -4
- package/scripts/write-handoff +2 -1
- package/templates/agents.md +6 -6
- package/templates/render.css +10 -0
- package/bin/plastic.js +0 -70
- package/skills/agent-advisor/SKILL.md +0 -84
- package/skills/auto/SKILL.md +0 -297
- package/skills/auto/evals/evals.json +0 -255
- package/skills/auto/references/end-tail.md +0 -64
- package/skills/conventions/SKILL.md +0 -29
- package/skills/dashboard/SKILL.md +0 -180
- package/skills/dashboard/evals/evals.json +0 -38
- package/skills/dashboard/references/classification.md +0 -22
- package/skills/dashboard/templates/dashboard-global.md +0 -20
- package/skills/dashboard/templates/dashboard-project.md +0 -19
- package/skills/direct/SKILL.md +0 -66
- package/skills/direct/references/request-signals.md +0 -59
- package/skills/doctor/SKILL.md +0 -305
- package/skills/doctor/report.md +0 -102
- package/skills/feedback/SKILL.md +0 -98
- package/skills/feedback/references/transport-and-privacy.md +0 -65
- package/skills/feedback/report.md +0 -36
- package/skills/install/SKILL.md +0 -215
- package/skills/intent-continuing/SKILL.md +0 -156
- package/skills/intent-continuing/references/board-fill.md +0 -52
- package/skills/intent-continuing/references/boarding-matrix.md +0 -35
- package/skills/intent-continuing/references/context-management.md +0 -28
- package/skills/intent-continuing/references/liveness-ranking.md +0 -57
- package/skills/intent-creating/SKILL.md +0 -89
- package/skills/intent-creating/evals/evals.json +0 -72
- package/skills/intent-creating/references/lifecycle.md +0 -81
- package/skills/intent-creating/references/wikilinks.md +0 -8
- package/skills/intent-ending/SKILL.md +0 -182
- package/skills/intent-ending/evals/evals.json +0 -74
- package/skills/intent-executing/SKILL.md +0 -87
- package/skills/intent-executing/evals/evals.json +0 -66
- package/skills/intent-executing/implementer-prompt.md +0 -47
- package/skills/intent-executing/spec-reviewer-prompt.md +0 -27
- package/skills/intent-speccing/SKILL.md +0 -136
- package/skills/intent-speccing/evals/evals.json +0 -126
- package/skills/intent-speccing/references/design-principles.md +0 -44
- package/skills/intent-speccing/references/per-section-fill-rules.md +0 -92
- package/skills/intent-speccing/references/self-verify-checklist.md +0 -37
- package/skills/project-creating/SKILL.md +0 -162
- package/skills/project-creating/references/hubs-projects.md +0 -55
- package/skills/project-creating/references/project-scaffolding.md +0 -97
- package/skills/releasing/SKILL.md +0 -376
- package/skills/releasing/references/deprecations.md +0 -60
- package/skills/releasing/references/promotion-and-tagging.md +0 -70
- package/skills/releasing/references/release-lines.md +0 -105
- package/skills/roadmap/SKILL.md +0 -90
- package/skills/roadmap/references/file-format.md +0 -134
- package/skills/roadmap/references/operations.md +0 -112
- package/skills/rollback/SKILL.md +0 -91
- package/skills/tutorial/SKILL.md +0 -66
- package/skills/tutorial/evals/evals.json +0 -186
- package/skills/uninstall/SKILL.md +0 -75
- package/skills/update/SKILL.md +0 -126
- /package/{skills/auto/references → docs/help}/agent-report-contract.md +0 -0
- /package/{skills/intent-executing → docs/help}/code-quality-reviewer-prompt.md +0 -0
- /package/{skills/conventions/references → docs/help}/knowledge-graph.md +0 -0
- /package/{skills/conventions/references → docs/help}/lifecycle-and-savepoints.md +0 -0
- /package/{skills/conventions/references → docs/help}/locks-and-worktrees.md +0 -0
- /package/{skills/conventions/references → docs/help}/maintenance-and-revisions.md +0 -0
- /package/{skills/intent-executing → docs/help}/plan-reviewer-prompt.md +0 -0
package/bin/plastic
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: UTF-8
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# Plastic skill usage census (intent 324). Counts typed /plastic-* commands
|
|
6
|
+
# from the prompt history against agent-invoked Skill calls, loads, and
|
|
7
|
+
# attribution from the transcript tree, so D19 (which Plastic verbs stay
|
|
8
|
+
# visible as slash commands) is decided on measured use.
|
|
9
|
+
#
|
|
10
|
+
# Maintainer tool: it reads the paths given to it and is never installed
|
|
11
|
+
# into ~/.plastic. This file never resolves the running process's home
|
|
12
|
+
# directory or its home environment variable: `bin/` ships in the npm
|
|
13
|
+
# tarball, so a default rooted there would make a published executable
|
|
14
|
+
# read any installer's transcripts. `--history` and `--transcripts` are
|
|
15
|
+
# required, with no default.
|
|
16
|
+
#
|
|
17
|
+
# Usage: bin/plastic-skill-census --history PATH --transcripts DIR
|
|
18
|
+
# [--skills-dir DIR] [--format md|json] [--out PATH]
|
|
19
|
+
# [--cutoff YYYY-MM-DD]
|
|
20
|
+
#
|
|
21
|
+
# --history PATH ~/.claude/history.jsonl, or a fixture (required)
|
|
22
|
+
# --transcripts DIR ~/.claude/projects, or a fixture tree (required)
|
|
23
|
+
# --skills-dir DIR a skill roster directory (default: this repo's skills/)
|
|
24
|
+
# --format md|json report format (default: md)
|
|
25
|
+
# --out PATH write the report here instead of stdout
|
|
26
|
+
# --cutoff YYYY-MM-DD exclude records on or after this date (default: 2026-09-02)
|
|
27
|
+
#
|
|
28
|
+
# Exit codes: 0 a report was produced, 1 a named path does not exist,
|
|
29
|
+
# 2 bad usage or a missing required flag.
|
|
30
|
+
|
|
31
|
+
require_relative "lib/skill_census"
|
|
32
|
+
|
|
33
|
+
USAGE = <<~TEXT
|
|
34
|
+
usage: plastic-skill-census --history PATH --transcripts DIR [--skills-dir DIR] [--format md|json] [--out PATH] [--cutoff YYYY-MM-DD]
|
|
35
|
+
|
|
36
|
+
--history PATH ~/.claude/history.jsonl, or a fixture (required)
|
|
37
|
+
--transcripts DIR ~/.claude/projects, or a fixture tree (required)
|
|
38
|
+
--skills-dir DIR a skill roster directory (default: this repo's skills/)
|
|
39
|
+
--format md|json report format (default: md)
|
|
40
|
+
--out PATH write the report here instead of stdout
|
|
41
|
+
--cutoff YYYY-MM-DD exclude records on or after this date (default: #{SkillCensus::DEFAULT_CUTOFF})
|
|
42
|
+
--help this message
|
|
43
|
+
|
|
44
|
+
Exits 0 on a report, 1 when a named path is missing, 2 on bad usage.
|
|
45
|
+
TEXT
|
|
46
|
+
|
|
47
|
+
def fail_usage(message)
|
|
48
|
+
warn "plastic-skill-census: #{message}"
|
|
49
|
+
warn USAGE
|
|
50
|
+
exit 2
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
history = nil
|
|
54
|
+
transcripts = nil
|
|
55
|
+
skills_dir = File.expand_path("../skills", __dir__)
|
|
56
|
+
format = "md"
|
|
57
|
+
out_path = nil
|
|
58
|
+
cutoff = SkillCensus::DEFAULT_CUTOFF
|
|
59
|
+
|
|
60
|
+
argv = ARGV.dup
|
|
61
|
+
until argv.empty?
|
|
62
|
+
case (flag = argv.shift)
|
|
63
|
+
when "--help", "-h"
|
|
64
|
+
puts USAGE
|
|
65
|
+
exit 0
|
|
66
|
+
when "--history"
|
|
67
|
+
history = argv.shift
|
|
68
|
+
fail_usage("--history needs a path") if history.to_s.empty?
|
|
69
|
+
when "--transcripts"
|
|
70
|
+
transcripts = argv.shift
|
|
71
|
+
fail_usage("--transcripts needs a path") if transcripts.to_s.empty?
|
|
72
|
+
when "--skills-dir"
|
|
73
|
+
skills_dir = argv.shift
|
|
74
|
+
fail_usage("--skills-dir needs a path") if skills_dir.to_s.empty?
|
|
75
|
+
when "--format"
|
|
76
|
+
format = argv.shift
|
|
77
|
+
fail_usage("--format must be md or json") unless %w[md json].include?(format)
|
|
78
|
+
when "--out"
|
|
79
|
+
out_path = argv.shift
|
|
80
|
+
fail_usage("--out needs a path") if out_path.to_s.empty?
|
|
81
|
+
when "--cutoff"
|
|
82
|
+
cutoff = argv.shift
|
|
83
|
+
fail_usage("--cutoff needs a YYYY-MM-DD date") if cutoff.to_s.empty?
|
|
84
|
+
else
|
|
85
|
+
fail_usage("unknown argument #{flag}")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
fail_usage("--history is required") if history.nil?
|
|
90
|
+
fail_usage("--transcripts is required") if transcripts.nil?
|
|
91
|
+
|
|
92
|
+
begin
|
|
93
|
+
roster = SkillCensus::Roster.load(skills_dir)
|
|
94
|
+
history_scan = SkillCensus::HistoryScanner.new(history, cutoff: cutoff).scan
|
|
95
|
+
transcript_scan = SkillCensus::TranscriptScanner.new(transcripts, cutoff: cutoff).scan
|
|
96
|
+
built = SkillCensus::Tally.new(history_scan, transcript_scan, roster).build
|
|
97
|
+
|
|
98
|
+
report = if format == "json"
|
|
99
|
+
SkillCensus::Report.json(built, history_path: history, transcripts_path: transcripts, cutoff: cutoff)
|
|
100
|
+
else
|
|
101
|
+
SkillCensus::Report.markdown(built, history_path: history, transcripts_path: transcripts, cutoff: cutoff)
|
|
102
|
+
end
|
|
103
|
+
rescue RuntimeError => error
|
|
104
|
+
warn "plastic-skill-census: #{error.message}"
|
|
105
|
+
exit 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
if out_path
|
|
109
|
+
File.write(out_path, report)
|
|
110
|
+
else
|
|
111
|
+
puts report
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
exit 0
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "open3"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
6
|
+
class VerifyChange
|
|
7
|
+
USAGE = <<~TEXT
|
|
8
|
+
Usage: bin/verify-change [BASE] [--test FILE]...
|
|
9
|
+
|
|
10
|
+
Checks only the code changed since the merge base of BASE and HEAD
|
|
11
|
+
(BASE defaults to origin/main, then main, then master):
|
|
12
|
+
|
|
13
|
+
1. lint on the changed Ruby files, in the project's style guide
|
|
14
|
+
2. the tests for the changed files, plus every --test FILE, with coverage on
|
|
15
|
+
3. patch coverage of the changed lines (simplecov patch)
|
|
16
|
+
4. mutation testing of the changed lines (mutineer, operators from .mutineer.yml)
|
|
17
|
+
5. CRAP scores of the changed methods (bin/crap)
|
|
18
|
+
|
|
19
|
+
Runs RSpec specs in a project with spec/ and .rspec, spec/spec_helper.rb,
|
|
20
|
+
or spec/rails_helper.rb, or with rspec, rspec-core, or rspec-rails under
|
|
21
|
+
DEPENDENCIES in Gemfile.lock, and Minitest tests otherwise. Warns when
|
|
22
|
+
both test/ and spec/ exist. Pass --test for the integration or system
|
|
23
|
+
tests of a feature the change touches.
|
|
24
|
+
|
|
25
|
+
A lint failure is reported but does not stop the other checks. Lint is
|
|
26
|
+
skipped, not failed, with no style guide, two style guides, no changed
|
|
27
|
+
Ruby files, or the linter gem missing from Gemfile.lock.
|
|
28
|
+
|
|
29
|
+
Every child runs under a throwaway HOME and PLASTIC_TMP, so a test or a
|
|
30
|
+
mutant that spawns an installer cannot reach the real home directory.
|
|
31
|
+
|
|
32
|
+
Exit codes: 0 verified, 1 a check failed, 2 usage or git error.
|
|
33
|
+
TEXT
|
|
34
|
+
|
|
35
|
+
PATCH_COVERAGE_MINIMUM = 100
|
|
36
|
+
MUTATION_THRESHOLD = 75
|
|
37
|
+
CRAP_THRESHOLD = 30
|
|
38
|
+
TESTS_STEP = "Tests with coverage"
|
|
39
|
+
LINT_STEP = "Lint"
|
|
40
|
+
DEFAULT_BASES = %w[origin/main main master].freeze
|
|
41
|
+
ALL_OPERATORS = %w[arithmetic comparison boolean_connector boolean_literal statement_removal return_nil
|
|
42
|
+
literal_mutation condition_negation string_literal regex collection_method].freeze
|
|
43
|
+
SOURCE_PATH = %r{\A(app|lib|tools|scripts|bin/lib)/.+\.rb\z}
|
|
44
|
+
RSPEC_DEPENDENCY = /^ rspec(-core|-rails)?(\s|!|$)/
|
|
45
|
+
|
|
46
|
+
Suite = Data.define(:name, :folder, :test_path, :locations, :suffix)
|
|
47
|
+
MINITEST = Suite.new(name: "Minitest", folder: "test/", test_path: %r{\Atest/.+_test\.rb\z}, suffix: "_test.rb",
|
|
48
|
+
locations: { %r{\Aapp/} => ["test/"], %r{\Alib/} => ["test/", "test/lib/"], %r{\Atools/} => ["test/tools/"],
|
|
49
|
+
%r{\Ascripts/lib/} => ["test/", "test/lib/"], %r{\Abin/lib/} => ["test/"], %r{\Ascripts/} => ["test/"] })
|
|
50
|
+
RSPEC = Suite.new(name: "RSpec", folder: "spec/", test_path: %r{\Aspec/.+_spec\.rb\z}, suffix: "_spec.rb",
|
|
51
|
+
locations: { %r{\Aapp/} => ["spec/"], %r{\Alib/} => ["spec/", "spec/lib/"], %r{\Atools/} => ["spec/tools/"] })
|
|
52
|
+
|
|
53
|
+
Step = Struct.new(:title, :env, :command)
|
|
54
|
+
LintDecision = Struct.new(:step, :skip_reason)
|
|
55
|
+
Plan = Struct.new(:sources, :tests, :untested, :lint_files, :lint_decision, :steps)
|
|
56
|
+
|
|
57
|
+
def self.test_candidates(source, suite = MINITEST)
|
|
58
|
+
suite.locations.flat_map do |prefix, targets|
|
|
59
|
+
source.match?(prefix) ? targets.map { |target| source.sub(prefix, target).sub(/\.rb\z/, suite.suffix) } : []
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialize(argv, root: Dir.pwd, out: $stdout, err: $stderr, git: nil, run: nil, sandbox: nil)
|
|
64
|
+
@argv = argv.dup
|
|
65
|
+
@root = root
|
|
66
|
+
@out = out
|
|
67
|
+
@err = err
|
|
68
|
+
@git = git || ->(*args) { Open3.capture2e("git", "-C", root, *args) }
|
|
69
|
+
@run = run || ->(env, command) { system(env, *command, chdir: root) }
|
|
70
|
+
@sandbox = sandbox
|
|
71
|
+
@owned_sandbox = sandbox.nil?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# A throwaway home for every child this script spawns. A gate step runs the
|
|
75
|
+
# project's own tests, and a test (or a mutant that defeats an injected
|
|
76
|
+
# runner) can spawn an installer, which resolves ~/.plastic and ~/.claude
|
|
77
|
+
# from HOME. Pointing HOME at a fresh directory is what keeps the real home
|
|
78
|
+
# out of reach; PLASTIC_TMP does the same for the session bridge files.
|
|
79
|
+
def sandbox
|
|
80
|
+
@sandbox ||= { "HOME" => Dir.mktmpdir("verify-change-home"),
|
|
81
|
+
"PLASTIC_TMP" => Dir.mktmpdir("verify-change-tmp") }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def call
|
|
85
|
+
return usage(0) if @argv.include?("--help") || @argv.include?("-h")
|
|
86
|
+
|
|
87
|
+
requested_base, extra_tests = parse_arguments
|
|
88
|
+
return usage(2) unless requested_base
|
|
89
|
+
|
|
90
|
+
base = merge_base(requested_base)
|
|
91
|
+
return 2 unless base
|
|
92
|
+
|
|
93
|
+
report_untracked
|
|
94
|
+
chosen = suite
|
|
95
|
+
report_both_suites(chosen)
|
|
96
|
+
plan = plan(changed: changed_files(base), extra_tests: extra_tests, base: base, rails: rails?, suite: chosen)
|
|
97
|
+
execute(plan, requested_base)
|
|
98
|
+
ensure
|
|
99
|
+
discard_sandbox
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def discard_sandbox
|
|
103
|
+
return unless @owned_sandbox && @sandbox
|
|
104
|
+
|
|
105
|
+
@sandbox.each_value { |dir| FileUtils.remove_entry(dir, true) }
|
|
106
|
+
@sandbox = nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def plan(changed:, extra_tests:, base:, rails:, suite: MINITEST)
|
|
110
|
+
sources = changed.grep(SOURCE_PATH).select { |path| exist?(path) }
|
|
111
|
+
lint_files = changed.select { |path| File.extname(path) == ".rb" && exist?(path) }
|
|
112
|
+
mapped = sources.to_h { |source| [source, self.class.test_candidates(source, suite).select { |path| exist?(path) }] }
|
|
113
|
+
changed_tests = changed.grep(suite.test_path)
|
|
114
|
+
tests = (changed_tests + mapped.values.flatten + extra_tests).uniq.select { |path| exist?(path) }
|
|
115
|
+
covered_elsewhere = !extra_tests.empty? || changed_tests.any?
|
|
116
|
+
untested = covered_elsewhere ? [] : mapped.select { |_source, found| found.empty? }.keys
|
|
117
|
+
Plan.new(sources, tests, untested, lint_files, lint_decision(lint_files), steps(sources, tests, base, rails, suite))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def parse_arguments
|
|
123
|
+
base = nil
|
|
124
|
+
extra_tests = []
|
|
125
|
+
arguments = @argv.dup
|
|
126
|
+
until arguments.empty?
|
|
127
|
+
argument = arguments.shift
|
|
128
|
+
if argument == "--test"
|
|
129
|
+
file = arguments.shift
|
|
130
|
+
return [nil, []] unless file
|
|
131
|
+
|
|
132
|
+
extra_tests << file
|
|
133
|
+
elsif argument.start_with?("-") || base
|
|
134
|
+
return [nil, []]
|
|
135
|
+
else
|
|
136
|
+
base = argument
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
[base || default_base, extra_tests]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def default_base
|
|
143
|
+
DEFAULT_BASES.find { |ref| @git.call("rev-parse", "--verify", "--quiet", ref).last.success? } || DEFAULT_BASES.last
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def merge_base(ref)
|
|
147
|
+
output, status = @git.call("merge-base", ref, "HEAD")
|
|
148
|
+
return output.strip if status.success?
|
|
149
|
+
|
|
150
|
+
@err.puts "Cannot find the merge base of #{ref} and HEAD: #{output.strip}"
|
|
151
|
+
nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def changed_files(base)
|
|
155
|
+
@git.call("diff", "--name-only", base, "--", "*.rb").first.split("\n")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def report_untracked
|
|
159
|
+
untracked = @git.call("ls-files", "--others", "--exclude-standard", "--", "*.rb").first.split("\n")
|
|
160
|
+
return if untracked.empty?
|
|
161
|
+
|
|
162
|
+
@err.puts "Not checked, because git does not track them yet (run git add first): #{untracked.join(", ")}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def report_both_suites(chosen)
|
|
166
|
+
return unless File.directory?(File.join(@root, "test")) && File.directory?(File.join(@root, "spec"))
|
|
167
|
+
|
|
168
|
+
skipped = chosen == RSPEC ? MINITEST : RSPEC
|
|
169
|
+
@err.puts "Both test/ and spec/ exist. Checking with #{chosen.name} only, so changed files under #{skipped.folder} do not run."
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def steps(sources, tests, base, rails, suite)
|
|
173
|
+
steps = []
|
|
174
|
+
steps << step(TESTS_STEP, { "COVERAGE" => "1" }, test_command(tests, rails, suite)) unless tests.empty?
|
|
175
|
+
return steps if sources.empty?
|
|
176
|
+
|
|
177
|
+
steps << step("Patch coverage", {}, %W[bundle exec simplecov patch --base #{base} --minimum #{PATCH_COVERAGE_MINIMUM}])
|
|
178
|
+
steps << step("Mutation testing", {}, mutation_command(sources, tests, base, rails, suite))
|
|
179
|
+
steps << step("CRAP scores", {}, %W[bundle exec bin/crap --threshold #{CRAP_THRESHOLD} --since #{base}] + sources)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def step(title, env, command)
|
|
183
|
+
Step.new(title, sandbox.merge(env), command)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def test_command(tests, rails, suite)
|
|
187
|
+
return %w[bundle exec rspec] + tests if suite == RSPEC
|
|
188
|
+
return %w[bin/rails test] + tests if rails
|
|
189
|
+
|
|
190
|
+
%w[bundle exec ruby -Ilib -Itools -Itest -e] + [%(require "test_helper"; ARGV.each { |file| require File.expand_path(file) })] + tests
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def lint_decision(lint_files)
|
|
194
|
+
return LintDecision.new(nil, "no changed Ruby files to lint") if lint_files.empty?
|
|
195
|
+
return LintDecision.new(nil, "two style guides found: .standard.yml and .rubocop.yml") if ambiguous_style_guide?
|
|
196
|
+
|
|
197
|
+
if rubocop_file?
|
|
198
|
+
return lint_gem_available?(:rubocop) ? LintDecision.new(lint_step(:rubocop, lint_files), nil) : LintDecision.new(nil, "the rubocop gem is not in Gemfile.lock")
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
if standard_file? || dependencies_section.match?(/^ standard(\s|!|$)/)
|
|
202
|
+
return lint_gem_available?(:standard) ? LintDecision.new(lint_step(:standard, lint_files), nil) : LintDecision.new(nil, "the standard gem is not in Gemfile.lock")
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
LintDecision.new(nil, "no style guide found")
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Local adaptation (intent 363): RuboCop reads .rubocop_with_todo.yml here, which
|
|
209
|
+
# is .rubocop.yml plus the generated todo. The todo holds this repository's 16,116
|
|
210
|
+
# pre-existing offences, so a lint run reports what the change did and nothing
|
|
211
|
+
# else. .rubocop.yml, the todo-free config, is what the repository is measured
|
|
212
|
+
# against as the todo shrinks.
|
|
213
|
+
def lint_step(guide, lint_files)
|
|
214
|
+
command = guide == :standard ? %w[bundle exec standardrb --force-exclusion] : rubocop_command
|
|
215
|
+
step(LINT_STEP, {}, command + lint_files)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def rubocop_command
|
|
219
|
+
base = %w[bundle exec rubocop --force-exclusion]
|
|
220
|
+
exist?(".rubocop_with_todo.yml") ? base + %w[--config .rubocop_with_todo.yml] : base
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def lint_gem_available?(command_gem)
|
|
224
|
+
return true if command_gem == :rubocop && (dependencies_section.match?(/^ rubocop(\s|!|$)/) ||
|
|
225
|
+
dependencies_section.match?(/^ standard(\s|!|$)/) || dependencies_section.match?(/^ rubocop-rails-omakase(\s|!|$)/))
|
|
226
|
+
return true if command_gem == :standard && dependencies_section.match?(/^ standard(\s|!|$)/)
|
|
227
|
+
|
|
228
|
+
false
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def ambiguous_style_guide?
|
|
232
|
+
standard_file? && rubocop_file? && !rubocop_config_uses_standard?
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def rubocop_config_uses_standard?
|
|
236
|
+
config = rubocop_config
|
|
237
|
+
return false if config.empty?
|
|
238
|
+
|
|
239
|
+
config.match?(/inherit_gem:\s*\{?\s*standard:/m) ||
|
|
240
|
+
config.match?(/^(plugins|require):\s*\n(?:[ \t]*-[ \t]*\S+[ \t]*\n)*?[ \t]*-[ \t]*standard[ \t]*$/m) ||
|
|
241
|
+
config.match?(/^(plugins|require):\s*\[[^\]]*\bstandard\b[^\]]*\]/m)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def rubocop_config
|
|
245
|
+
exist?(".rubocop.yml") ? File.read(File.join(@root, ".rubocop.yml")) : ""
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def standard_file?
|
|
249
|
+
exist?(".standard.yml")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def rubocop_file?
|
|
253
|
+
exist?(".rubocop.yml")
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def dependencies_section
|
|
257
|
+
return @dependencies_section if defined?(@dependencies_section)
|
|
258
|
+
|
|
259
|
+
lockfile = exist?("Gemfile.lock") ? File.read(File.join(@root, "Gemfile.lock")) : nil
|
|
260
|
+
unless lockfile
|
|
261
|
+
return @dependencies_section = ""
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
lines = lockfile.lines
|
|
265
|
+
start = lines.index { |line| line.strip == "DEPENDENCIES" }
|
|
266
|
+
unless start
|
|
267
|
+
return @dependencies_section = ""
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
section = []
|
|
271
|
+
lines[(start + 1)..].each do |line|
|
|
272
|
+
break if line.strip.empty?
|
|
273
|
+
|
|
274
|
+
section << line
|
|
275
|
+
end
|
|
276
|
+
@dependencies_section = section.join
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def mutation_command(sources, tests, base, rails, suite)
|
|
280
|
+
%w[bundle exec mutineer run] + sources + tests.flat_map { |test| ["--test", test] } +
|
|
281
|
+
%W[--since #{base} --threshold #{MUTATION_THRESHOLD}] + (suite == RSPEC ? %w[--framework rspec] : []) +
|
|
282
|
+
(rails ? %w[--rails] : %w[--strategy redefine]) +
|
|
283
|
+
(exist?(".mutineer.yml") ? [] : ["--operators", ALL_OPERATORS.join(",")])
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def execute(plan, requested_base)
|
|
287
|
+
if plan.sources.empty? && plan.tests.empty? && plan.lint_files.empty?
|
|
288
|
+
@out.puts "No changed Ruby files since #{requested_base}."
|
|
289
|
+
return 0
|
|
290
|
+
end
|
|
291
|
+
unless plan.untested.empty?
|
|
292
|
+
@err.puts "No test file found for: #{plan.untested.join(", ")}. Write one, or pass --test FILE for the tests that cover it."
|
|
293
|
+
return 1
|
|
294
|
+
end
|
|
295
|
+
unless exist?(".mutineer.yml") || plan.sources.empty?
|
|
296
|
+
@err.puts "Missing .mutineer.yml: passing all 11 operators on the command line instead."
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
@out.puts "Base: #{requested_base}"
|
|
300
|
+
@out.puts "Sources: #{plan.sources.empty? ? "none" : plan.sources.join(", ")}"
|
|
301
|
+
@out.puts "Tests: #{plan.tests.join(", ")}"
|
|
302
|
+
@out.puts "Home: #{sandbox["HOME"]}"
|
|
303
|
+
FileUtils.rm_rf(File.join(@root, "coverage"))
|
|
304
|
+
failed = []
|
|
305
|
+
if plan.lint_decision.step
|
|
306
|
+
@out.puts "\n== #{LINT_STEP}"
|
|
307
|
+
failed << plan.lint_decision.step unless @run.call(plan.lint_decision.step.env, plan.lint_decision.step.command)
|
|
308
|
+
elsif plan.lint_decision.skip_reason
|
|
309
|
+
@out.puts "\nLint skipped: #{plan.lint_decision.skip_reason}"
|
|
310
|
+
end
|
|
311
|
+
plan.steps.each do |step|
|
|
312
|
+
@out.puts "\n== #{step.title}"
|
|
313
|
+
next if @run.call(step.env, step.command)
|
|
314
|
+
|
|
315
|
+
failed << step
|
|
316
|
+
next unless step.title == TESTS_STEP
|
|
317
|
+
|
|
318
|
+
@out.puts "\nThe tests are red, so the other checks did not run: a failing test kills every mutant."
|
|
319
|
+
break
|
|
320
|
+
end
|
|
321
|
+
@out.puts failed.empty? ? "\nChange verified" : "\nFailed: #{failed.map(&:title).join(", ")}"
|
|
322
|
+
failed.empty? ? 0 : 1
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def rails?
|
|
326
|
+
exist?("config/application.rb")
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def suite
|
|
330
|
+
listed = dependencies_section.match?(RSPEC_DEPENDENCY)
|
|
331
|
+
spec = File.directory?(File.join(@root, "spec")) && (exist?(".rspec") || exist?("spec/spec_helper.rb") || exist?("spec/rails_helper.rb"))
|
|
332
|
+
spec || listed ? RSPEC : MINITEST
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def exist?(path)
|
|
336
|
+
File.exist?(File.join(@root, path))
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def usage(code)
|
|
340
|
+
(code.zero? ? @out : @err).puts USAGE
|
|
341
|
+
code
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
exit VerifyChange.new(ARGV).call if $PROGRAM_NAME == __FILE__
|
package/deprecations.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Plastic deprecation notices
|
|
2
2
|
# Each entry is shown to the user during SessionStart.
|
|
3
|
-
# See
|
|
3
|
+
# See CONTRIBUTING.md's "Cut a release" section for the full convention.
|
|
4
4
|
#
|
|
5
5
|
# Schema:
|
|
6
6
|
# id: unique-slug (used for dismissal tracking)
|
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
*Companion to the Operating Manual. How to use Fable as your advisor and planner.*
|
|
4
4
|
|
|
5
|
-
*Adapted for Plastic (intent 185): this is the shipped reference copy
|
|
6
|
-
`plastic
|
|
5
|
+
*Adapted for Plastic (intent 185): this is the shipped reference copy, reached with
|
|
6
|
+
`plastic help advisor-protocol`. Two named agents carry it,
|
|
7
7
|
`plastic-primary-advisor` (Primary Advisor) and `plastic-secondary-advisor`
|
|
8
8
|
(Secondary Advisor, the explicit escalation); the
|
|
9
9
|
answer shape below shapes the brief and the answer on whichever one you
|
|
10
|
-
dispatch, never which file to pick.
|
|
11
|
-
|
|
12
|
-
(`advisor.claude.default`, falling back to `plastic-primary-advisor`) to route your
|
|
13
|
-
consultation automatically; name a
|
|
14
|
-
specific advisor in your request to override it. A frontier model rewards a
|
|
10
|
+
dispatch, never which file to pick. There is no automatic routing between them: the user or
|
|
11
|
+
the main session dispatches one deliberately, by naming it. A frontier model rewards a
|
|
15
12
|
single, well-formed one-shot brief more than a back-and-forth, so front-load
|
|
16
13
|
everything section 3 below asks for before you send. The rest of this
|
|
17
14
|
document is the owner's protocol, unchanged.*
|
|
@@ -28,9 +28,9 @@ in 2.0, intent 304; the lead writes the Why and How record itself):
|
|
|
28
28
|
reviewed before code; dispatches the executor; applies the risk rule; closes.
|
|
29
29
|
- **plastic-executor** (Exec): commits the matrix's tests red, writes the code, checks off
|
|
30
30
|
`checklist.md`, appends `## Insights`, and drives the suite green.
|
|
31
|
-
- **the plan reviewer**: a fresh agent on
|
|
32
|
-
`plan-reviewer-prompt.md`, an optional dispatch before any code exists.
|
|
33
|
-
- **the post-execution reviewer**: a fresh agent on `code-quality-reviewer-prompt.md`,
|
|
31
|
+
- **the plan reviewer**: a fresh agent on the auto skill's
|
|
32
|
+
`references/plan-reviewer-prompt.md`, an optional dispatch before any code exists.
|
|
33
|
+
- **the post-execution reviewer**: a fresh agent on `references/code-quality-reviewer-prompt.md`,
|
|
34
34
|
dispatched only when the auto skill's risk rule fires; never the maker.
|
|
35
35
|
|
|
36
36
|
One agent boot (the executor) is the minimum delivery; the plan reviewer is a second,
|
|
@@ -118,14 +118,14 @@ written path, and the lead verifies state from the files (`plastic-lock status`,
|
|
|
118
118
|
### Delegation
|
|
119
119
|
|
|
120
120
|
The roles are thin handoff contracts, not a spawning engine. Dispatch runs through Plastic's
|
|
121
|
-
own engine, `plastic
|
|
121
|
+
own engine, `plastic intent step`: one executor for the consolidated action, the two
|
|
122
122
|
reviewer prompts as fresh agents. The team model defines who hands what to whom and where the
|
|
123
123
|
reviews sit; the engine does the actual spawning.
|
|
124
124
|
|
|
125
125
|
### Fallback by Case
|
|
126
126
|
|
|
127
127
|
If the harness supports agent dispatch, auto mode dispatches through
|
|
128
|
-
`plastic
|
|
128
|
+
`plastic intent step`. If the harness has no agent dispatch at all (Codex CLI today), the
|
|
129
129
|
lead walks the five steps itself: it still writes the matrix and the tests first, and reviews
|
|
130
130
|
its own plan against the matrix before code, saying so in `## Insights`.
|
|
131
131
|
|
|
@@ -147,8 +147,8 @@ tests first, one suite run per intent.
|
|
|
147
147
|
## Autonomous Delivery
|
|
148
148
|
|
|
149
149
|
Human owns What and Why for human-initiated intents. The team assists (research, exploration)
|
|
150
|
-
but the human drives until handoff. When Why is complete, or the human
|
|
151
|
-
the auto team takes over How and Exec autonomously.
|
|
150
|
+
but the human drives until handoff. When Why is complete, or the human runs
|
|
151
|
+
`plastic auto take ID`, the auto team takes over How and Exec autonomously.
|
|
152
152
|
|
|
153
153
|
- **Safe-by-default:** the executor always prefers non-destructive routes (rename vs delete,
|
|
154
154
|
additive migrations, backups before changes). Destructive actions on existing projects
|
|
@@ -39,4 +39,4 @@ INDEX but the lock is still present or stale). Finishing the tail is FINISHING a
|
|
|
39
39
|
a done intent is never moved back to `## Active`.
|
|
40
40
|
|
|
41
41
|
One report per audience: a delivery produces `outcome.md` plus one EM-to-CTO owner report, and
|
|
42
|
-
no other step restates either (see `
|
|
42
|
+
no other step restates either (see `plastic help human-report-contract`).
|
|
@@ -29,27 +29,25 @@ written by eye:
|
|
|
29
29
|
|
|
30
30
|
## Binding table (intent 331f)
|
|
31
31
|
|
|
32
|
-
Every
|
|
33
|
-
|
|
32
|
+
Every command or lead role that shows state names its own report verb, one row per binding
|
|
33
|
+
and trigger. Each one carries the SAME rule next to its verb: print the screen as the first
|
|
34
34
|
characters of the reply, nothing before it, no fence, or the hook cannot paint it.
|
|
35
35
|
|
|
36
|
-
|
|
|
36
|
+
| Command or lead | Trigger | Verb |
|
|
37
37
|
|---|---|---|
|
|
38
|
-
| `plastic
|
|
39
|
-
| `plastic
|
|
40
|
-
| `plastic
|
|
41
|
-
| `plastic
|
|
42
|
-
| `plastic
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
| `plastic
|
|
47
|
-
| `plastic
|
|
48
|
-
| `plastic
|
|
49
|
-
| `plastic
|
|
50
|
-
| `plastic
|
|
51
|
-
| `plastic-dashboard` | any invocation | `dashboard.rb ... --screen` |
|
|
52
|
-
| `plastic-intent-executing` | after the red commit, and after the suite | `report-screen state` |
|
|
38
|
+
| `plastic continue` | project route (continue, load project) | `dashboard.rb ... --screen` |
|
|
39
|
+
| `plastic continue` | a named intent | `report-screen state` |
|
|
40
|
+
| `plastic continue` | "where are we" (a status ask) | `report-screen session` |
|
|
41
|
+
| `plastic continue` | "why so long" | `report-screen delay` |
|
|
42
|
+
| `plastic continue` | a roadmap route | `report-screen roadmap ... state` |
|
|
43
|
+
| the auto team's lead | the How boundary, before the executor | `report-screen plan` |
|
|
44
|
+
| the auto team's lead | each of the five triggers | `report-screen state` |
|
|
45
|
+
| the auto team's lead | close | `report-screen delivered` |
|
|
46
|
+
| `plastic intent end` | the close | `report-screen delivered` |
|
|
47
|
+
| `plastic intent spec` | the action files are written | `report-screen plan` |
|
|
48
|
+
| `plastic roadmap show` | any invocation | `report-screen roadmap ... state` |
|
|
49
|
+
| `plastic status` | any invocation | in-process (`Scope#stores`) |
|
|
50
|
+
| `plastic intent step` | after the red commit, and after the suite | `report-screen state` |
|
|
53
51
|
|
|
54
52
|
## A roadmap's own three reports (intent 331c)
|
|
55
53
|
|
|
@@ -104,7 +102,7 @@ A delivery still ends with `outcome.md` plus one `delivered` screen.
|
|
|
104
102
|
|
|
105
103
|
A delivery produces exactly two artifacts: `outcome.md` (generated by `scripts/end-intent`
|
|
106
104
|
from `graph.md`, `nodes/`, and the ledger when the intent has one, intent 339; hand-authored
|
|
107
|
-
by `plastic
|
|
105
|
+
by `plastic intent end` otherwise) and one `delivered` screen at the End stage. No stage or skill restates a delivery already
|
|
108
106
|
written to `outcome.md`; point at it instead. Skills do not open with a banner that names the
|
|
109
107
|
skill or restates the intent id and name the owner just typed. Announce only what the reader
|
|
110
108
|
cannot already know: an error, a result, a choice with its reason, or a handoff.
|
|
@@ -6,8 +6,8 @@ This chapter holds the full roadmap file format and its relationship to INDEX.md
|
|
|
6
6
|
|
|
7
7
|
Roadmaps exist for planned parallel delivery of intents in a coherent and organized way. A roadmap
|
|
8
8
|
is a named, ordered, delivery-side collection of intents: the delivery-side counterpart to a
|
|
9
|
-
release (completion-side, tracked in `CHANGELOG.md`).
|
|
10
|
-
|
|
9
|
+
release (completion-side, tracked in `CHANGELOG.md`). Create one by hand from the template, then
|
|
10
|
+
use `plastic roadmap show`, `next`, `log`, and `check` to read, drive, and audit it.
|
|
11
11
|
|
|
12
12
|
File location: `roadmaps/{slug}.md`, a sibling of `INDEX.md`, wherever `INDEX.md` lives, never
|
|
13
13
|
inside `store/` (store holds intent directories, not project artifacts). For a project that is its
|