Generate Foundation Plan subject IDs - #4
Conversation
Foundation Plan authors need stable identities before adding independently mutable subjects, but the Skill should not invent or persist them itself. Expose the existing UUIDv7 generator as a pure leaf command with no project or network access. Document the continuity rules and exercise both direct and packed-package paths at the minimum runtime.
Review: technicalReviewed the single commit Verified
Design notes
|
Lesson: what this PR actually didOne command that prints one ID. The ideas worth keeping are why a tool should do less than it could, and why "generate an ID" belongs in code rather than in instructions. The one-sentence version
Why this needs to exist at allEvery independently mutable thing in a Foundation Plan carries a So when an agent adds a new Entity to a Plan, it needs a new ID. The Skill in the sibling repository could have said "generate a UUIDv7". That instruction has a predictable failure mode: an agent asked to produce a random value may produce something that looks right, or copy one from a nearby example, and both corrupt the identity model quietly. Which is why the Skill instead says to check whether the CLI offers The transferable idea: when correctness depends on a value being generated properly, put the generation in code you can test, and have the instructions call it. Prose is a bad random number generator. Doing less on purposeThe interesting engineering here is subtraction. The command's help text says it "reads no files and makes no network request", and the code makes that true in a way worth looking at. Before this change, the CLI resolved the working directory when dispatching any if (argv[0] === "init") return runPlanInit({ ..., cwd: cwd ?? getCwd() });
if (argv[0] === "push") return runPlanPush({ ..., cwd: cwd ?? getCwd() });
if (argv[0] === "subject-id") return runPlanSubjectId({ /* no cwd at all */ });I confirmed the behavior: run it in an empty directory and it prints an ID and creates nothing. Why bother, when That principle generalizes past this command. When you add a feature to an existing dispatcher, check whether the shared setup code at the top is work your new path actually needs. Shared setup is convenient and it quietly widens the failure surface of everything downstream of it. Verifying an ID generatorTesting "it returns a UUID" is easy and nearly worthless. The properties that matter here are specific, so I checked them directly on five successive outputs:
That last one is the property the identity design actually relies on, and it is the one a naive implementation gets wrong while still producing something that passes a "looks like a UUID" regex. If you ever review an ID generator, ordering and version bits are where to spend your attention. |
|
The structured-authoring path enabled here now has pending server support: firstdraft/firstdraft#190 imports Entities, |
|
Cross-repository dogfood now exercises the exact packed artifact from A fresh Skill-guided author used The packed CLI also handled both negative paths correctly: a 422 capability response preserved the rejected Plan and left CLI state unchanged, and a cloned stale writer received 412, stopped without retry/reinit, and left its old state unchanged. This supersedes the earlier lack of an end-to-end nonempty dogfood run; it does not change this PR's narrow subject-ID scope. |
Summary
firstdraft plan subject-id, which prints one fresh lowercase UUIDv7 and performs no Plan, state, working-directory, or network access.sketch/0.19continuity rule: retainsubject_uuidthrough renames and same-kind moves, and mint a new value for a replacement concept.Why
The pre-Compilation Skill needs a safe way to obtain IDs before adding independently mutable authored subjects. Keeping generation in this zero-runtime-dependency CLI avoids making the Skill invent randomness or mutate the Plan implicitly.
This command enables local authoring only. The current server slice still rejects nonempty Plans, as documented in firstdraft/skills#1.
Verification
The final review also observed that the pre-existing
initandpushhelp paths resolve the working directory before parsing. This branch does not regress that behavior and strictly removes working-directory lookup from root andsubject-idpaths, so I kept that separate from this narrow slice.Stack