Auto-deploy: 2026-08-05 17:08:23
This commit is contained in:
+6
-2
@@ -167,8 +167,12 @@ const PROFILE_DATA = {
|
||||
bullets: [
|
||||
{ text: 'Delivered 30+ production-grade mobile applications across e-commerce, delivery, healthcare and news sectors, acting as technical decision-maker for early-stage clients.', tags: ['delivery', 'leadership', 'mobile', 'scale'] },
|
||||
{ text: 'Migrated legacy Android (Java) applications to Flutter, cutting maintenance complexity and delivery timelines by roughly 50%.', tags: ['flutter', 'migration', 'mobile'] },
|
||||
{ text: 'Built custom Flutter plugins and tailored API layers to integrate with complex legacy backend systems.', tags: ['flutter', 'integration', 'architecture'] },
|
||||
{ text: 'Owned App Store and Google Play release management end-to-end, from store listings and review submission through versioned production rollout.', tags: ['mobile', 'release', 'ios', 'android', 'delivery'] }
|
||||
// Ordered by how often each is actually screened for in mobile roles.
|
||||
// When the tailoring model returns fewer bullets than the page allows,
|
||||
// the generator tops up in this order — so the more commonly required
|
||||
// evidence fills the slot first.
|
||||
{ text: 'Owned App Store and Google Play release management end-to-end, from store listings and review submission through versioned production rollout.', tags: ['mobile', 'release', 'ios', 'android', 'delivery'] },
|
||||
{ text: 'Built custom Flutter plugins and tailored API layers to integrate with complex legacy backend systems.', tags: ['flutter', 'integration', 'architecture'] }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
+28
-1
@@ -401,7 +401,8 @@ PROMPT;
|
||||
if ($b !== '') $bullets[] = $b;
|
||||
}
|
||||
}
|
||||
if (empty($bullets)) {
|
||||
$fromAi = !empty($bullets);
|
||||
if (!$fromAi) {
|
||||
foreach ($role['bullets'] as $b) {
|
||||
$bullets[] = $b['text'];
|
||||
}
|
||||
@@ -413,6 +414,32 @@ PROMPT;
|
||||
$cap = $d['caps'][$ri] ?? 2;
|
||||
$bullets = array_slice($bullets, 0, $cap);
|
||||
|
||||
// The model also errs the other way, returning fewer bullets than
|
||||
// the layout has room for. An empty slot is wasted page space, and
|
||||
// worse, it can strand a skill in the Technical Skills line with no
|
||||
// supporting evidence anywhere in the experience. Top up from the
|
||||
// master bullets the model did not pick, keeping its ordering first.
|
||||
// Which master bullets did the model already use? Match on the
|
||||
// "roleIndex.bulletIndex" ids it returns, not on the text: it
|
||||
// rewrites wording to carry the job's keywords, so comparing strings
|
||||
// would treat a rewritten bullet as a new one and print the same
|
||||
// fact twice. Without usable ids we cannot tell what was used, so we
|
||||
// leave the role as-is rather than risk a duplicate.
|
||||
$usedIdx = [];
|
||||
foreach (($aiRoles[$ri]['bullet_ids'] ?? []) as $bid) {
|
||||
$parts = explode('.', (string)$bid);
|
||||
if (count($parts) === 2) $usedIdx[] = (int)$parts[1];
|
||||
}
|
||||
|
||||
if ($fromAi && $usedIdx && count($bullets) < $cap) {
|
||||
foreach ($role['bullets'] as $bi => $master) {
|
||||
if (count($bullets) >= $cap) break;
|
||||
if (!in_array($bi, $usedIdx, true)) {
|
||||
$bullets[] = $master['text'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$experienceBlock .= '<ul>';
|
||||
foreach ($bullets as $b) {
|
||||
$experienceBlock .= '<li>' . e($b) . '</li>';
|
||||
|
||||
+10
-10
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user